servlet概述
Servlet (Server Applet),全称Java Servlet。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。
服务器上需要一些程序,如根据用户输入访问数据库的程序。在Servlet API产生之前,这些通常是使用公共网关接口(Common Gateway Interface,CGI)应用程序完成的。CGI技术用于创建动态web应用程序。CGI技术有许多缺点,如为每个请求创建单独的进程、依赖于平台代码(C、C++)、内存消耗大和性能低等。
CGI 和 Servlet
>Servlet在处理时间、内存利用率等方面表现更好。因为servlet使用多线程技术,为每个请求创建一个新线程。这自然就比为每个请求创建新进程的CGI技术要快很多,并且节省内存资源。
>Servlet是平台无关的,使用Servlet开发的web应用程序可以运行在任何标准的web容器中,如Tomcat、JBoss、Glassfish服务器。同样可以在任何操作系统中,如Windows、Linux、Unix、Solaris、Mac等。
>Servlet是健壮的,因为servlet容器负责管理servlet的生命周期,我们不需要担心内存泄漏、安全、垃圾收集等问题。
>Servlet是易维护的,并且学习曲线小。因为在使用Servlet的时候我们只需要关注业务逻辑就可以了。
Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
自MVC规范出现后,Servlet的作用被明确为仅仅作为控制器使用,不在生成页面标签,也不作为视图层角色使用。
Servlet 的主要功能在于交互式地浏览和修改数据,生成动态 Web 内容。这个过程为:
① 客户端发送请求至服务器端;
② 服务器将请求信息发送至 Servlet;
③ Servlet 生成响应内容并将其传给服务器。响应内容动态生成,通常取决于客户端的请求;
④服务器将响应返回给客户端。
一个 Servlet 就是 Java 编程语言中的一个类,它被用来扩展服务器的性能,服务器上驻留着可以通过“请求-响应”编程模型来访问的应用程序。虽然 Servlet 可以对任何类型的请求产生响应,但通常只用来扩展 Web 服务器的应用程序。
Servlet API
javax.servlet.Servlet是Servlet API的最上层接口。还有一些其他的接口和类是我们在使用servlet的时候需要关注的。在Servlet 3.0规范中,建议使用的注解我们也需要了解。先了解一下Servlet API层次结构。
Servlet接口
javax.servlet.Servlet 是Servlet API的最上层接口,Servlet接口定义了一系列servlet的生命周期方法(init、service、destory等)。所有的Servlet类都需要继承这个接口。
该接口中定义了以下方法:
public abstract void init(ServletConfig paramServletConfig) throws ServletException- 该方法由servlet容器调用,用于初始化servlet以及servlet配置参数。在init()方法执行之前,servlet是无法处理用户请求的。在servlet生命周期中该方法只会被调用一次,他会使servlet类不同区别于普通的java对象。我们可以扩展该方法来初始化资源,如数据库连接、socket连接等。
public abstract ServletConfig getServletConfig() - 该方法返回一个servlet配置对象,其中包含servlet中所有初始化参数和启动配置。我们可以用这个方法来获取servlet的初始化参数,这些参数一般被定义在web.xml或servlet 3的注解中。后面会介绍ServletConfig接口。
public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException- 该方法负责处理客户端请求。当servlet容器收到客户端请求时,它会创建一个新线程并执行service()方法,并把request 和 response作为参数传递给该方法。servlet通常运行在多线程环境中,所以开发人员应该使用同步来保证访问共享资源的线程安全性问题。
public abstract String getServletInfo() - 这个方法返回包含servlet信息的字符串,比如它的作者、版本和版权。返回的字符串应该是纯文本,不能有标记符号。
public abstract void destroy() - 这个方法在整个servlet生命周期中只会被调用一次来关闭所有资源。有点像Java中的finalize方法。
GenericServlet抽象类
GenericServlet抽象类实现了Servlet接口、ServletConfig接口和Serializable接口。GenericServlet使得开发人员更加容易地编写自定义servlets,它提供了生命周期的init()和destroy()方法以及ServletConfig接口的方法的“简易版本”,该类中定义的大部分方法都是让用户更放方便的使用Servlet和ServletConfig接口中定义的常用方法。开发者自定义的servlet只要继承该抽象类,然后重写service()方法即可。
GenericServlet里主要实现了Servlet接口、ServletConfig接口的方法,还实现了log()方法,该方法在ServletContext接口里声明。
GenericServlet里还有一个重要的方法——无参数的init方法。如果我们必须在处理请求之前初始化一些资源,那么可以重写该方法。
HTTPServlet抽象类
HTTPServlet 类是GenericServlet类的子类,开发者通过开发自定类继承该抽象类,来创建基于HTTP的web应用程序。继承HTTPServlet抽象类的子类至少要重写一个HTTPServlet抽象类里的方法。
HTTPServlet抽象类里的结构。
通常客户端的请求主要有GET和POST请求,自定义Servlet类为了响应这两种请求,必须重写doGet()和doPost()方法,默认调用doGet方法。get方法会在地址栏中显示用户名和密码,post方法不会,在安全性上get小于post;提交内容的大小上get小于post,get提交的数据小于2k,post提交的数据理论上不受限制,实际应用中一般不大于64k;响应速度上get要快于post,get要求服务器立即处理请求,而post请求可能形成一个队列请求。
如果自定义Servlet类为了响应4种方式的请求,则需要重写上面的4个方法。
一般情况下,自定义Servlet类对应所有请求的响应都是一样的。这样可以重写一个方法来代替上面的几个方法,即只要重写service()方法及可响应客户端的所用请求。
- init(ServletConfig config): 创建Servlet实例时,调用该方法初始化Servlet资源;
- destroy(): 消耗Servlet实例时,自动调用该方法的回收资源。
通常这两个方法不需要重写,除了要在初始化Servlet时,完成某些资源初始化的方法,才考虑重写init()方法。如果要在销毁Servlet之前,先完成某些资源的回收,比如数据库连接等,才需要重写destroy()方法。
不用为自定义Servlet类写构造器,如果需要初始化该自定义Servlet类,应将初始化操作放在自定义Servlet类的init()方法中定义。如果重写了init(ServletConfig config)方法,则应在重写该方法的第一行调用super.init(config),该方法将调用HttpServlet的init方法。
ServletConfig 接口
当一个servlet初始化期间servlet容器会通过servlet配置对象传递信息给该servlet。
ServletConfig用于描述servlet本身的相关配置信息。每个servlet都有属于它自己的ServletConfig对象,该对象由servlet容器负责实例化。可以在web.xml中提供初始化参数,当然在servlet3.0中可以使用注解。我们可以使用getServletConfig()方法来获取ServletConfig的对象。看该接口里的结构。
ServletContext 接口
ServletContext接口用于描述应用程序的相关信息。ServletContext是一个独立的对象,可用于web应用程序中所有的servlet。当我们想要一些初始化的参数可用于web应用程序中多个或全部servlet时,我们可以使用ServletContext对象并且在web.xml中使用标签定义参数。我们可以通过ServletConfig 中的 getServletContext()方法得到ServletContext对象。
接口结构。
使用Servlet示例
package com.afy.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FirstServletDemo extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("=========init without parameters=======");
super.init();
}
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("=========init with parameters=======");
super.init(config);
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("========= service =======");
PrintWriter pw = response .getWriter();
pw.println("Hi there,would you wanna play a game?");
pw.close();
}
@Override
public void destroy() {
System.out.println("=========destroy=======");
super.destroy();
}
}
FirstServletDemo类继承了HttpServlet类,表明它可以作为Servlet使用。其中定义了service方法来响应用户请求,通过HttpServletRequest获取客户端的form请求参数与,并显示请求参数的值。
Servlet与JSP相比:
- Servlet中没有内置对象,原理JSP中的内置对象都必须有程序显式创建;
- 对应静态的HTML标签,Servlet都必须使用页面输出流逐行输出。
JSP是Servlet的简化,使用JSP只需程序员输出到客户端的内容,至于JSP脚本如何嵌入一个类中,则有JSP容器完成。而Servlet是个完整的Java类,这个类的service()方法用于生成对客户端的响应。
普通Servlet类里的service方法的作用等同于JSP生成Servlet类的_jspServlet()方法。
Servlet的配置
写好的Servlet源文件不能响应用户的请求,必须将其编译成class文件。一般.class文件放在WEB-INF/classes路径下
要Servlet响应用户请求,必须将Servlet配置在Web应用中。配置Servlet一般是在web.xml文件中操作的。
Servlet3.0开始,配置Servlet有两种方式
- 在Servlet类中使用@WebServlet注解进行配置
@WebServlet(name="FirstServletDemo",urlPattern={"/Hi"})
- 通过web.xml文件配置
因此,配置一个响应客户请求的Servlet,至少需要配置两个元素:Servlet的名字和Servlet的URL。
以上示例中,如果在web.xml中配置了Servlet的名字和URL,则该Servlet的URL为/Hello,如果没有配置web.xml文件,则该Servlet类上的@WebServlet注解就会起作用,该Servlet的URL为/Hi。
JSP/Servlet的生命周期
JSP本质就是Servlet,写好的JSP页面将由Web容器编译成对应的Servlet,当Servlet在容器中运行时,其实例的创建和销毁等都不是由程序员决定的,而是由Web容器进行控制的。
创建Servlet实例有两种情况
- 客户端第一次请求某个Servlet时,系统创建Servlet的实例:大部分的Servlet是这种情况创建的;
- Web应用启动时立即创建Servlet实例,即load-on-startup Servlet。
每个Servlet的运行都遵循如下生命周期。
1. 创建Servlet实例;
2. Web容器调用Servlet的init方法,对Servlet进行初始化;
3. Servlet初始化后,将一直存在容器中,用于响应客户端请求。如果客户端发送Get请求,容器调用Servlet的doGet方法处理并响应请求;如果客户端发送Post请求,容器调用Servlet的doPost方法处理并响应请求。或者统一使用service()方法处理响应用户请求。
4. Web容器决定销毁Servlet时,先调用Servlet的destroy方法,通常在关闭Web应用之时销毁Servlet。
load-on-startup Servlet
我们已经知道创建Servlet实例有两个时机:用户请求时或应用启动时。应用启动时就创建Servlet,通常用于某些后台服务的Servlet,或者需要拦截很多请求的Servlet;这种Servlet通常作为应用的基础Servlet使用,提供重要的后台服务。配置load-on-startup的Servlet有两种方式。
- 在web.xml文件中通过< servlet…/>元素的< load-on-startup…/>子元素进行配置;
- 通过@WebServlet注解的loadOnStartup属性指定。
< load-on-startup…/>元素或loadOnStartup属性都只接收一个整型值,这个整型值越小,Servlet就越优先优化实例化。
代码示例
@WebServlet(loadOnStartup=1)
public class TimerServlet extends HttpServlet{
public void init(ServletConfig config)throws ServletException{
super.init(config);
Timer t = new Timer(1000,new ActionListener(){
public void actionPerformed(ActionEvent e){
System.out.println(new Date());
}
});
t.start();
}
}
这个Servlet没有提供service()方法,这表明它不能响应用户请求,所以无须为它配置URL映射。该Servlet仅仅执行计数器功能,每隔一段时间会在控制台打印出当前时间。由于它不能接收用户请求,所以只能在应用启动时实例化。
web.xml文件中配置代码
< servlet>
<load-on-startup>1</load-on-startup>
< /servlet>