1、Servlet接口实现类
Servlet接口Sun公司定义了两个默认实现类,分别是:GenericServlet、HttpServlet。
HttpServlet指能够处理HTTP请求的servlet,它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。
HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法,因此,开发人员在编写Servlet时,通常只需要覆写doGet或者doPost方法,而不要去覆写service方法。
2、Servlet的注册和运行
1)注册Servlet
在web.xml文件中,一个<servlet>元素用于注册一个Servlet,<servlet>元素中包含有两个主要的子元素,<servlet-name>和<servlet-class>它们分别用于设置Servlet的注册名称和指定的完整类名。例如:
<servlet>
<servlet-name>ServletDemo</servlet-name>
<servlet-class>com.servlet.ServletDemo</servlet-class>
</servlet>
2)、映射Servlet
在web.xml文件中,一个<servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,客户端将使用Servlet所映射的对外访问路径来访问Servlet,而不是使用Servlet名称来访问Servlet。<servlet-mapping>元素中包含有两个子元素:<servlet-name>和<url-patterm>,它们分别用于指定Servlet的注册名称和设置Servlet的对外访问路径,如下所示:
<servlet-mapping>
<servlet-name>ServletDemo</servlet-name>
<url-pattern>/ServletDemo</url-pattern>
</servlet-mapping>
同一个Servlet可以被映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子元素的设置可以使同一个Servlet的注册名。在Servlet映射到的URL中也可以使用*通配符,但是只能 有两种固定的格式:一种格式是“*.扩展名”,两一种是以正斜杠(/)开头并以/*结尾。当Servlet引擎收到一个访问请求时,它将采用最具体匹配原则来查找请求URL的映射,其中,“*.扩展名”匹配优先级最低。
3、类装载器
Java虚拟机使用每一个类的第一件事情就是将该类的字节码装载进来,装载类字节码的功能是由类装载器完成的,类装载器负责根据一个类的名称来定位和生成类的字节码数据后返回给Java虚拟机。最常见的类装载器是将要加载的类名转换成一个.class文件名,然后从文件系统中找到该文件并读取其中的内容。这种类装载器也不是直接将.class文件中的内容原封不动地返回给Java虚拟机,它需要将.class文件中的内容转换成Java虚拟机使用的类字节码。
Class类用于描述Java程序语言中使用的一个类的有关信息,它里面定义了对其所描述的类进行各种操作的方法。要想在程序中获得代表某个类的字节码数据的class实例对象,可以采用下面三种方式:
①:类名.class 例如:System.class
②:对象.getClass(),例如,new Date().getClass()
③:Class.forName(“类名”) 例如:Class.forName(“java.util.Date”)
类装载器本身也是一个Java类,Java类库中提供了一个java.lang.ClassLoader来作为类装载器的基类,Java虚拟机和程序都调用ClassLoader类的loadClass方法来加载类,ClassLoader是一个抽象类,真正的类装载器必须是ClassLoader的子类,Class中定义了一个getClassLoader方法,用于返回它所描述的类的类装载器对象,这个返回对象的类型就是ClassLoader。
笔者总结的一段非常重要的话:依据一个类的存放位置,这个类最总只能由一个特定的类装载器装载。对于一个已被父级类装载器装载的类来说,Java虚拟机默认也使用这个父级类装载器去装载它所调用的其他类,由于父级类装载器不会委托子级类装载器去装载类,所以,一般情况下,一个已被负积累装载器装载的类无法调用哪些职能被子级类装载器发现和装载的其他类。
每个运行中的线程都有一个关联的上下文类装载器,可以使用Thread.setContextClassLoader()方法设置线程的上下文类装载器,每个线程默认的上下文类装载器是其父线程的上下文类装载器,而主线程的类装载器初始被设置为ClassLoader.getSystemClassLoader()方法返回的系统来装载器。
4、区分Servlet的编译环境和运行环境
编译和运行Servlet的环境是完全不同的两个环境,解释执行Servlet的过程是在Tomcat服务器程序中进行的,Servlet的运行环境就是Tomcat服务器程序所设置的环境;编译Servlet是有编程人员使用javac命令完成的,Servlet的编译环境是某个命令行窗口或开发工具所设置的环境,即使Servlet程序在运行时能够成功装载某个其他类,但在编译时也不一定能够找到这个类。
5、Servlet的线程安全问题
Servlet引擎采用多线程模式运行,它为并发的每个访问请求都预备一个独立的线程来进行响应。Servlet引擎采用多线程模式可以提高访问性能,但也带来了线程安全的问题,如果并发的多个请求访问的是同一个Servlet,那么将有多个线程来并发调用该Servlet的service方法。
Servlet API中定义了一个javax。Servlet。SingleThreadModel接口,如果某个Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用其service方法。SingleThreadModel接口中没有定义任何方法,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。但是Servlet引擎仍然支持对该Servlet多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Servlet实例对象,这样,可以同时有多个Servlet实例对象并发运行。但每个实例对象的service方法都不会被多个线程并发调用,真正意义上的多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。所以,如果多个并发的service方法调用有线程安全问题,最好是自己在service方法中编写多线程同步控制代码,而不要采用多线程的运行模式。
注意:哪些情况下要考虑线程安全问题?
在Servlet程序中除了访问成员变量时要注意线程安全问题,访问其他共享资源时也应注意线程安全问题,例如:web应用程序对象,Session对象,数据库等都可能涉及多个Servlet线程同时访问的问题,如果Servlet中没有定义成员变量和service方法中没有访问无同步访问控制的共享数据时,即使多个线程并发调用service方法,也不会存在任何线程安全问题。
我还在javaweb学习中,如果有不正确或者不恰当的地方,请前辈们多多指教,非常感谢!