servlet相关(流程、原理、生命周期、HttpServlet)
一、什么是servlet ?
Servlet本质上就是一个Java语言定义的接口,广义的Servlet就是指实现了这个Servlet接口的类。一般指的就是后者。
Servlet实例的主要功能就是根据客户端的请求,找到并调用服务端相关java代码,完成对请求的处理和运算。
Servlet实例遵循了服务器能够识别的规则,服务器会根据请求调用对应的servlet进行请求处理。
(1)Servlet实例,由Servlet容器创建,通常这个容器就是tomcat。
(2)Servlet是一个接口:位于javax.servlet包中。
二、Servlet实现步骤
具体实现一个Servlet的步骤如下:
- 创建普通的java类,并实现Servlet接口
- 重写service方法,添加业务逻辑代码
- 在webRoot下的WEB-INF文件夹下的web.xml中配置Servlet及其映射
// 普通Java类实现Servlet接口
public class MyServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
// 添加业务逻辑代码
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("MyServlet 的 service()方法");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
<servlet-name>是指servlet的名称,<servlet>标签和<servlet-mapping>标签中的<servlet-name>必须一致。<servlet-class>是当前servlet的全限定类名,<url-pattern>是映射servlet的url。
三、Servlet生命周期
Servlet生命周期是指servlet实例的创建、初始化、服务执行、销毁的整个过程。
这些流程都是web服务器负责调用执行,程序员无法控制整个流程,但是程序员可以获取到这些Servlet生命周期的时间点,在这些时间点上可以做些业务相关的操作。
理解Servlet的生命周期还要看下Servlet的源码,源码中对其生命周期中的主要节点都有相应的方法:
整个方法调用的流程图如下:
当一个客户端请求过来的时候,tomcat服务器首先解析这个url,首先找到对应的应用app,然后根据/xxx去web.xml中根据配置信息去找对应的servlet,找到对应的servlet后,若还没有创建servlet实例就会新建一个该servlet实例,然后执行init()方法,service()方法,然后响应客户端;若该servlet实例已存在,就只会执行service()方法。当应用卸载的时候,会执行destory()方法销毁这个servlet实例。
我们来演示一下:
public class MyServlet implements Servlet {
public MyServlet(){
System.out.println("create servlet !");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("initialize Servlet !");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("MyServlet's service() method");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("destroy servlet!");
}
}
浏览器输入地址:
查看日志:
create servlet !
initialize Servlet !
MyServlet's service() method
可以看出执行顺序符合上面流程图定义的顺序,无参构造--》初始化--》service方法,但是没有直接执行destroy销毁方法,一个servlet实例只会在服务停止时调用一次该方法。无参构造和初始化也只会执行一次,若现在再次刷页面,只会执行service方法:
create servlet !
initialize Servlet !
MyServlet's service() method
MyServlet's service() method
MyServlet's service() method
总结一下servlet的特征:
- servlet是单例多线程的,单例是指servlet的实例只有一个,多线程是指每次客户端的请求,web服务器都会从线程池中分配一个工作线程去执行servlet的service()方法,线程安全。
- 默认情况下,servlet不会在服务启动时实例化,即一个servlet实例的无参构造和初始化也只会执行一次,且不会在服务启动时执行,而是在第一次请求时执行,所以接到请求时会先检查servlet实例是否已经存在
- 每次请求只会执行一个service方法
- 一个servlet实例只会在服务停止时调用一次该方法
四、HttpServlet
现在我们的请求都是基于HTTP协议的,所以应该专门为HTTP请求写一个Servlet做为通用父类。
HttpServlet的工作流程如下:
- 1.Web客户向Servlet容器发出Http请求
- 2.Servlet容器解析Web客户的Http请求
- 3.Servlet容器创建一个HttpRequest对象,在这个对象中封装Http请求信息
- 4.Servlet容器创建一个HttpResponse对象
- 5.Servlet容器调用HttpServlet的service方法,把HttpRequest和HttpResponse对象作为service方法的参数传给HttpServlet对象,service()方法会判断来自客服端的请求方式,根据不同请求方式调用不同方法,如果是get请求,则调用doGet()方法,如果是post请求,则调用doPost()方法
- 6.HttpServlet调用HttpRequest的有关方法,获取HTTP请求信息
- 7.HttpServlet调用HttpResponse的有关方法,生成响应数据
- 8.Servlet容器把HttpServlet的响应结果传给Web客户
补充一:
JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么?
JSP (JavaServer Pages)是Servlet技术的扩展,本质上是Servlet的简易方式,更强调应用的外表表达。JSP编译后是"类servlet"。Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。JSP侧重于视图,Servlet主要用于控制逻辑。
补充二:
- genericservlet类提供了servlet接口的基本实现;
- HttpServlet是GenericServlet的子类,又是在GenericServlet的基础上做了增强;
补充三:
请求转发与重定向区别
servlet API中forward() 与redirect()的区别?
前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。所以,前者更加高效,在前者可以满足需要时,尽量使用 forward()方法,并且,这样也有助于隐藏实际的链接。在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用 sendRedirect()方法。从数据共享来说:forward 共享 request 中的数据,redirect 不能。forward - 一般用于用户登陆的时候,根据角色转发到相应的模块, redirect - 一般用于用户注销登陆时返回主页面和跳转到其它的网址等;
补充四:Servlet与线程安全
因为一个类型的Servlet只有一个实例对象,那么就有可能会现时出一个Servlet同时处理多个请求,那么Servlet是否为线程安全的呢?答案是:“不是线程安全的”。这说明Servlet的工作效率很高,但也存在线程安全问题!
所以我们不应该在Servlet中随便创建成员变量,因为可能会存在一个线程对这个成员变量进行写操作,另一个线程对这个成员变量进行读操作。
- 1.不要在Servlet中创建成员!创建局部变量即可!
- 2.可以创建无状态成员!无状态表示不可变
- 3.可以创建有状态的成员,但状态必须为只读的!