什么是Servlet?
Servlet是Server Applet的简称,是用Java编写的服务器端程序,是JavaEE平台下的技术标准。其主要功能在于和浏览器交互并生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。
Servlet通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。从原理上来讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。Servlet有三种实现方式:实现Servlet接口、继承抽象类GenericServlet、继承HttpServlet。
Servlet在程序中到底处于一个什么地位?
1. Servlet是可以接收http请求并作出响应的一种技术,是JAVA语言编写的一种动态资源;
2. Servlet是前后端衔接的一种技术,不是所有的JAVA类都可以接收请求和作出响应,但是Servlet可以;
3. 在MVC模式中,Servlet作为Controller层(控制层)主要技术,用于和浏览器完成数据交互,控制交互逻辑;
Servlet的工作模式
- 客户端发送请求至服务器
- 服务器运行并调用Servlet,Servlet根据客户端请求生成响应内容并将其传给服务器,响应内容动态生成,通常取决于客户端的请求
- 服务器将响应返回客户端
Servlet体系结构与Tomcat的关系
Servlet是Tomcat的一个组件,Servlet的功能需要依赖一个servlet-api.jar,这个包是由Tomcat提供的,Tomcat在初始化Servlet时,首先读取web.xml文件,根据web.xml文件中的参数信息初始化ServletConfig、ServletContext对象,同时帮助我们创建HttpServletRequest和HttpServletResponse对象一并交给Servlet实例,此时,Servlet就具有了相关的功能。
Servlet API 概览
Servlet API 包含以下4个Java包:
1. javax.servlet:其中包含定义Servlet和Servlet容器之间契约的类和接口。
2. javax.servlet.http:主要定义了与HTTP协议相关的HttpServlet类,HttpServletRequest接口和HttpServletResponse接口。
3. javax.servlet.annotation: 其中包含标注Servlet、Filter、Listener的标注。它还为被标注元件定义元数据。
4. javax.servlet.descriptor:其中包含提供程序化登录Web应用程序的配置信息的类型。
Servlet的主要类型
Servlet、ServletConfig、ServletContext、ServletRequest、ServletResponse、GenericServlet(抽象类)
Servlet的继承结构
Servlet接口,只负责定义Servlet程序的访问规范;
GenericServlet抽象类实现了Servlet接口,做了很多空实现,并持有一个ServletConfig类的引用,并提供了一些ServletConfig的使用方法;
HttpServlet抽象类实现了service方法,并实现了请求分发处理;
Servlet的使用方法
Servlet技术的核心是Servlet,它是所有Servlet类必须直接或者间接实现的一个接口。在编写实现Servlet接口的类时,直接实现它,在扩展实现Servlet这个接口的类时,间接实现它。
Servlet的工作原理
Servlet接口定义了Servlet与Servlet容器之间的契约,这个契约是:Servlet容器将Servlet类载入内存,生成Servlet实例并调用它具体的方法。但是要注意的是,在一个应用程序中,每种Servlet类型只能有一个实例。
用户发起请求使Servlet容器调用Servlet的service()方法,并传入一个ServletRequest对象和一个ServletResponse对象。ServletRequest对象和ServletResponse对象都是由Servlet容器(例如Tomcat)封装好的,并不需要开发人员去实现,开发者可以直接使用这两个对象。
ServletRequest中封装了当前的Http请求,因此,开发人员不必解析和操作原始的Http数据。ServletResponse表示当前用户的Http响应,开发人员只需直接操作ServletResponse对象就能把响应轻松的返回给用户。
对于每一个应用程序,Servlet容器还会创建一个ServletContext对象,这个对象中封装了上下文(应用程序)的环境详情。每个应用程序只有一个ServletContext,每个Servlet对象也都有一个封装Servlet配置的ServletConfig对象。
Servlet 接口中定义的方法
我们首先来看一看Servlet接口中定义了哪些方法:
public interface Servlet {
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
其中,init( )、service( )、destroy( )是Servlet生命周期的方法,代表了Servlet从“诞生”到“执行”再到“销毁 ”的过程。
Servlet的生命周期
Servlet的生命周期是由容器管理的,Servlet容器(例如Tomcat)会根据下面的规则来调用这三个方法:
1. 初始化方法init( ),只会执行一次(启动Tomcat的时候默认是不执行的,在访问的时候才会执行)当Servlet第一次被请求时,Servlet容器会实例化这个Servlet,然后就会调用这个方法来初始化Servlet,但是这个方法在后续请求中不会在被Servlet容器调用,我们可以利用init()方法来执行相应的初始化工作。
2. 服务方法service( ),每当请求Servlet时,Servlet容器就会调用这个方法。第一次请求时,Servlet容器会先调用init( )方法初始化一个Servlet对象出来,然后会调用它的service( )方法进行工作,但在后续的请求中,Servlet容器只会调用service方法了。
3. 销毁方法destory(),当要销毁Servlet时,Servlet容器就会调用这个方法,卸载应用程序或者关闭Servlet容器时,就会发生这种情况,一般在这个方法中会写一些清除代码。
一般在实际项目开发中,都是使用继承HttpServlet类的方式去实现 Servlet 程序,下面编写一个简单的Servlet程序来验证一下它的生命周期:
public class ServletLifeCycle extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("ServletLifeCycle 初始化方法执行" );
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws
//执行服务
System.out.println("ServletLifeCycle service方法执行" );
}
@Override
public void destroy() {
System.out.println("ServletLifeCycle 销毁方法执行" );
}
@Override
public ServletConfig getServletConfig() {
return super.getServletConfig();
}
@Override
public String getServletInfo() {
return super.getServletInfo();
}
}
<servlet>
<servlet-name>ServletLifeCycle</servlet-name>
<servlet-class>com.cgcstudy.servlet.ServletLifeCycle</servlet-class>
<!-- <load-on-startup>6</load-on-startup>-->
</servlet>
<servlet-mapping>
<servlet-name>ServletLifeCycle</servlet-name>
<url-pattern>/servletLifeCycle.do</url-pattern>
</servlet-mapping>
在xml中配置正确的映射关系,在浏览器中访问Servlet,第一次访问时,控制台输出了如下信息:
再次访问Servlet,控制台输出的信息变成了下面这样:
接下来,关闭Servlet容器,控制台输出了Servlet的销毁信息:
这就是一个Servlet的完整生命周期。
通过以上案例发现Servlet默认情况下是在第一次访问时被创建并初始化的,如果初始化的工作比较多,那么第一次访问就会比较耗时,我们可否改变它的创建时机呢?
我们可以在web.xml文件中配置<load-on-startup>,使得Servlet在服务器启动时创建并初始化,<load-on-startup>节点必须写在<servlet>节点的内部,且作为最后一个节点,取值必须是整数,如果是大于等于0的整数表示在服务器启动时就被创建并初始化,如果有多个Servlet设置了<load-on-startup>节点的值,那么值越小越优先执行;如果是负数则表示第一次被访问时创建并初始化,也就是默认情况,可以省略不写。
总结
Servlet的生命周期是由容器管理的,Servlet从创建到销毁的全过程,共分为四个阶段:
1. 创建阶段:创建Servlet对象;
2. 初始化阶段:会执行init方法,只会执行一次。默认是在第一次访问Servlet时执行,可以在web.xml进行配置,配置<load-on-startup> 让Servlet随着Tomcat的启动而进行初始化,配置的值大于0即可,配置为负数就是默认情况,当多个 Servlet配置了<load-on-startup>值越小的优先执行;
3. 服务阶段:执行service方法,会执行多次;
4. 销毁阶段:会执行destroy方法,只会执行一次;
注意点
访问Servlet会实例化Servlet对象,发现构造方法只会执行一次,说明对象只创建了一
次,即单例模式。
Servlet 的其它两个方法:
getServletInfo(),这个方法会返回Servlet的一段描述,可以返回一段字符串;
getServletConfig(),这个方法会返回由Servlet容器传给init()方法的ServletConfig对象;
ServletConfig接口
ServletConfig对象对应web.xml文件中的<servlet>节点,当Servlet容器(如Tomcat)初始化Servlet时,会将该Servlet的配置信息,封装到一个ServletConfig对象中,我们可以通过该对象读取<servlet>节点中的配置信息
<servlet>
<servlet-name>servletName</servlet-name>
<servlet-class>servletClass</servlet-class>
<init-param>
<param-name>key</param-name>
<param-value>value</param-value>
</init-param>
</servlet>
当Servlet容器初始化Servlet时,Servlet容器会给Servlet的init( )方式传入一个ServletConfig对象,ServletConfig其中几个方法如下:
public interface ServletConfig {
String getServletName(); //获得Servlet在web.xml中配置的name值
ServletContext getServletContext(); //获得ServletContext对象
String getInitParameter(String var1); //获得Servlet的初始化参数
Enumeration<String> getInitParameterNames(); //获得所有Servlet的初始化参数的名称
}
servletConfig.getInitParameter("key"),该方法可以读取web.xml文件中<servlet>标签中<init-param>标签中的配置信息;
servletConfig.getInitParameterNames(),该方法可以读取web.xml文件中当前<servlet>标签中所有<init-param>标签中的值;
ServletContext
ServletContext官方叫Servlet上下文。ServletContext对象表示Servlet应用程序,服务器会为每一个Web应用创建一个ServletContext对象,每个Web应用程序都只有一个ServletContext对象,这个对象全局唯一,而且Web应用中的所有Servlet都共享这个对象,所以叫全局应用程序共享对象;在将一个应用程序同时部署到多个容器的分布式环境中,每台Java虚拟机上的Web应用都会有一个ServletContext对象。
那么为什么要存在一个ServletContext对象呢?因为有了ServletContext对象,可以利用该对象获取整个Web 应用程序的初始化信息、读取资源文件等,并且可以动态注册Web对象。这些信息是保存在ServletContext中的一个内部Map中,保存在ServletContext中的对象被称作属性。
ServletContext获取方式有两种:
1. request.getServletContext();
2. this.getServletContext();
ServletContext的作用:
1. 获取web.xml文件中的信息,可以为所有的Servlet提供初始化数据;
<context-param>
<param-name>key</param-name>
<param-value>value</param-value>
</context-param>
<context-param>配置是一组键值对, <context-param>的作用:这个元素用来声明应用范围内的上下文初始化参数,其中的 <param-name> 设定上下文的参数名称,这个必须是唯一的,而<param-value>设定的参数名称的值。当服务器启动时,服务器会读取web.xml配置,当读到<context-param></context-param>这个节点的时候,容器会将这个节点中配置的值set到ServletContext(上下文对象)中,这样我们在程序中就能通过这个上下文对象去取得我们这个配置值。
servletContext.getInitParameter("key"),该方法可以读取web.xml文件中<context-param>标签中的配置信息。
servletContext.getInitParameterNames(),该方法可以读取web.xml文件中所有 <param-name> 标签中的值。
<context-param>配置和<init-param>的区别:
我们可以看到<init-param>是放在一个Servlet内的,所以这个参数是只针对某一个Servlet而言的,所以它们的区别就有点像全局变量和局部变量的,<context-param>是针对整个项目,所有的Servlet都可以取得使用,<init-param>只能是在某个Servlet下面配置,就在那个Servlet里面调用。
2. 域对象,共享数据(ServletContext对象是最大的域对象,被Web应用中的所有Servlet共享,而且 ServletContext可以帮助我们实现数据的传递);
ServletContext中的下列方法负责处理属性:
Object getAttribute(String var1);
Enumeration<String> getAttributeNames(); //从全局容器中获取数据
void setAttribute(String var1, Object var2); //向全局容器中存放数据
void removeAttribute(String var1); //根据key删除全局容器中的value
ServletContext对象生命周期:当容器启动时会创建ServletContext对象并一直缓存该对象,直到容器关闭后该对象生命周期结束,ServletContext对象的生命周期非常长,所以在使用全局容器时不建议存放业务数据。
GenericServlet抽象类
我们可以通过实现Servlet接口来编写Servlet程序,但是,使用这种方法,必须要实现Servlet接口中定义的所有的方法,即使有一