六、在MyEclipse中建立servlet服务
我们可以直接建立servlet服务,首先新建一个web工程,然后在src中新建Servlet,然后选择覆写的方法,一般只需要选第一个和doGet、doPost方法。
ServletDemo3.java
package cn.itcast.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Servlet Demo3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getOutputStream().write("Hello Servlet!!!".getBytes());
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
web.xml文件会自动生成
<?xmlversion="1.0"encoding="UTF-8"?>
<web-appxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://xmlns.jcp.org/xml/ns/javaee"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"id="WebApp_ID"version="3.1">
<servlet>
<servlet-name>ServletDemo3</servlet-name>
<servlet-class>cn.itcast.servlet.ServletDemo3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo3</servlet-name>
<url-pattern>/servlet/ServletDemo3</url-pattern>
</servlet-mapping>
<display-name>day05</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
这里需要注意几个使用MyEclipse的几个小细节:
当我们建立一个servlet类之后,会覆写一些方法,而如果发现自动生成的方法中的参数不是我们想要的,比如arg0、arg1之类的,这是我们需要导入源码。可以直接按住ctrl键然后将鼠标置于类上(如HttpServlet)左键,然后点击attach source,然后选择External Folder,选择源码路径:E:\apache\apache-tomcat-8.0.26-src,点击ok,如果还是不出现源码则重启MyEclipse。
*** 注意***:一定要注意web.xml中类名的写法:cn.itcast.servlet.ServletDemo1,同时GenericServlet是普通的servlet实现,我们一般使用HttpServlet这个父类。
注意:当我们改动web.xml(web应用程序的配置文件)后,服务器会自动重启,但是当我们改动java类之后需要手动重新部署发布。同时我们在MyEclipse中的一个web应用程序中要写一个静态页面(1.html)时,需要将这个文件放在WebRoot目录下。
七、Servlet的一些细节
细节一##
由于客户端是通过url地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把Servlet程序映射到一个url地址上,这个工作在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成。
<servlet>元素用于注册Servlet,它包含两个主要的子元素,<servlet-name>和<servlet-class>,分别用于设置servlet的注册名称和servlet的完整名称。
一个<servlet-mapping>元素用于映射一个已注册的servlet的一个对外访问路径,它包含两个子元素:<servlet-name>和<url-pattern>,分别用于指定servlet的注册名称和servlet的对外访问路径。
细节二
同一个servlet可以被映射到多个url上,即多个<servlet-mapping>元素的<servlet-name>子元素的设置值可以是同一个servlet的注册名。
在servlet映射到url中也可以使用通配符,但是只能有两种固定的格式:一种格式是****.do,另一种是/****结尾。第一种情况是必须以.do 结尾,第二种情况若是/****则可以匹配任意地址字符串,若是/abc/****则必须以/abc/开头的任意字符串地址。
细节三
对于如下的一些映射关系:
Servlet1映射到/abc/****, Servlet2映射到/****,Servlet3映射到/abc,Servlet4映射到****.do。问题:*
(1)当请求url为/abc/a.html,/abc/****和/****都匹配,哪个servlet响应?
调用servlet1,这里是根据相似度来选择的。
(2)当请求url为/abc,/abc/**** 和/abc*** 都匹配,哪个servlet响应?
调用servlet3,这里也是根据相似度来选择的。
(3)当请求url为/abc/a.do,/abc/****和****.do都匹配,哪个servlet响应?
这里调用servlet1,这里是“/”的优先级比”.”要高。
(4)当请求url为/a.do,/**** 和****.do都匹配,哪个servlet响应?
调用servlet2,这里是“/”的优先级比”.”要高。
(5)当请求url为/xxx/vvv/a.do,/**** 和****.do 都匹配,哪个servlet响应?
调用servlet2,这里是“/”的优先级比”.”要高。
细节四
servlet是一个供其他java程序调用的java类,它不能独立运行,它的运行完全由servlet引擎来控制和调度。
针对客户端的多次servlet请求,通常情况下,服务器只会创建一个servlet实例对象,也就是说servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。
在servlet的整个声明周期,servlet的init方法只被调用一次。而对一个servlet的每次访问请求都导致servlet引擎被调用一次servlet的service方法。对于每次访问请求,service引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的servlet的service方法,service方法再根据请求方式分别调用doXXX方法。
细节五
如果在<servlet>元素中配置了一个<load-on-startup>元素,那么web应用程序在启动时,就会装载并创建servlet的实例对象、以及调用servlet实例对象的init方法。如:
<servlet>
<servlet-name>ServletDemo3</servlet-name>
<servlet-class>cn.itcast.servlet.ServletDemo3</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
用途是为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据。这在后面的框架中会用到。最好配置。
详解<load-on-startup>元素:
1)load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。
2)它的值必须是一个整数,表示servlet应该被载入的顺序
3)当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet;
4)当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。
5)正数的值越小,该servlet的优先级越高,应用启动时就越先加载。
6)当值相同时,容器就会自己选择顺序来加载。
所以,<load-on-startup>x</load-on-startup>,中x的取值1,2,3,4,5代表的是优先级,而非启动延迟时间。
细节六
如果某个servlet的映射路径仅仅为一个正斜杠(/),那么这个servlet就称为当前web应用程序的缺省servlet。
凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的url,它们的访问请求都将交给缺省的servlet处理,也就是说,缺省servlet用于处理所有其他servlet都不处理的访问请求。
在tomcat的配置文件web.xml中,注册了一个名称为org.apache.catalina.servlets.DefaultServlet的servlet,并将这个servlet设置为缺省servlet。
当访问tomcat服务器中的某个静态html文件和图片时,实际上是在访问这个缺省的servlet。
细节七
当多个客户端并发访问同一个servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。
如果某个servlet实现了SingleThreadModel接口,那么servlet引擎将以单线程模式来调用其service方法。其实这和我们使用同步(synchronized)的方式是一样的原理,但是这里注意: 子类不能抛出比父类更多的异常。
SingleThreadModel接口中没有定义任何方法,只要在servlet类的定义中增加实现SingleThreadModel接口的声明即可。
对于实现了SingleThreadModel接口的servlet,servlet引擎仍然支持对该servlet的多线程并发访问,其采用的方式是产生多个servlet实例对象,并发的每个线程分别调用一个独立的servlet实例对象。
实现SingleThreadModel接口并不能真正解决servlet的线程安全问题,因为servlet引擎会创建多个servlet实例对象,而真正意义上解决多线程安全问题是指一个servlet实例对象被多个线程同时调用的问题。事实上,在ServletAPI2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。