Servlet的基础详解与架构解析
Servlet的基础详解与架构解析
Servlet快速入门
什么是Servlet?
Servlet是基于Java技术的Web组件,由容器管理并产生动态的内容。Servlet与客户端通过Servlet容器实现的请求/响应模型进行交互。
SpringMVC框架的底层是基于Servlet实现的。
入门代码
1.在我们的项目中创建libs目录存放第三方的jar包
2.项目中导入servlet-api.jar libs目录中,就在我们tomcat安装的目录 中 lib 目录中(也可以直接使用Maven,就不用这么麻烦了)
3.创建servlet包,专门存放就是我们的servlet
4.创建IndexServlet,实现Servlet 重写方法
5.IndexServlet 类上加上@WebServlet("/Hello"),注解定义 URL访问的路径
6.重写Servlet 类中service ,在service中编写 动态资源
Tomcat的项目配置
IndexServlet类的详细代码
@WebServlet("/Hello")
public class IndexServlet 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 {
String username = servletRequest.getParameter("username");
servletResponse.setContentType("Text/html;charset=utf-8");
PrintWriter writer = servletResponse.getWriter();
if("Harmony".equals(username)) {
writer.println("可以访问");
} else {
writer.println("拒绝访问");
}
writer.close();
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
debug结果如下
@WebServlet注解
@WebServlet 用于将一个类声明为 Servlet,该注解会在部署时被容器处理,容器根据其具体的属性配置将相应的类部署为 Servlet。该注解具有下表给出的一些常用属性。
属性名 | 类型 | 标签 | 描述 | 是否必需 |
---|---|---|---|---|
name | String | <servlet-name> | 指定 Servlet 的 name 属性。 如果没有显式指定,则取值为该 Servlet 的完全限定名,即包名+类名。 | 否 |
value | String[ ] | <url-pattern> | 该属性等价于 urlPatterns 属性,两者不能同时指定。 如果同时指定,通常是忽略 value 的取值。 | 是 |
urlPatterns | String[ ] | <url-pattern> | 指定一组 Servlet 的 URL 匹配模式。 | 是 |
loadOnStartup | int | <load-on-startup> | 指定 Servlet 的加载顺序。 | 否 |
initParams | WebInitParam[ ] | <init-param> | 指定一组 Servlet 初始化参数。 | 否 |
asyncSupported | boolean | <async-supported> | 声明 Servlet 是否支持异步操作模式。 | 否 |
description | String | <description> | 指定该 Servlet 的描述信息。 | 否 |
displayName | String | <display-name> | 指定该 Servlet 的显示名。 | 否 |
注意:
- 通过实现 Serlvet 接口或继承 GenericServlet 创建的 Servlet 类无法使用 @WebServlet 注解。
@WebServlet 注解 和 web.xml 的优缺点
使用 web.xml 或 @WebServlet 注解都可以配置 Servlet, 两者各有优缺点。
@WebServlet 注解配置 Servlet
- 优点:@WebServlet 直接在 Servlet 类中使用,代码量少,配置简单。每个类只关注自身业务逻辑,与其他 Servlet 类互不干扰,适合多人同时开发。
- 缺点:Servlet 较多时,每个 Servlet 的配置分布在各自的类中,不便于查找和修改。
web.xml 配置文件配置 Servlet
- 优点:集中管理 Servlet 的配置,便于查找和修改。
- 缺点:代码较繁琐,可读性不强,不易于理解。
servlet 执行流程
问:servlet是谁创建的?service是谁在调用?
servlet是由我们的 web服务器(tomcat)创建、该方法是由我们的 web服务器(tomcat)调用
问:tomcat服务器如何执行到servlet中的service方法?
我们创建的servlet实现Servlet接口,重写了service方法。
所以总的来说,Servlet的执行流程:
客户端发起HTTP请求,请求到达Tomcat服务器,通过URL映射找到所对应的、具体的Servlet,再调用其service方法。
servlet 生命周期(重点)
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
- Servlet 初始化后调用 init () 方法。
- Servlet 调用 service() 方法来处理客户端的请求。
- Servlet 销毁前调用 destroy() 方法。
- 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
在 Servlet 的整个生命周期中,创建 Servlet 实例、init() 方法和 destory() 方法都只执行一次。当初始化完成后,Servlet 容器会将该实例保存在内存中,通过调用它的 service() 方法,为接收到的请求服务。
1. 创建Servlet实例
web容器负责加载Servlet,当web容器启动时或者是在第一次使用这个Servlet时,容器会负责创建Servlet实例,但是用户必须通过部署描述符(web.xml)指定Servlet的位置,或者在类上加上@WebServlet,成功加载后,web容器会通过反射的方式对Servlet进行实例化。
@WebServlet(urlPatterns = "/Hello",loadOnStartup = 1)
loadOnStartup=1:表示在Tomcat就初始化这个Servlet容器
@WebServlet(urlPatterns = "/mayiktmeite",loadOnStartup = -1)
若为负数(-1):第一次被访问时创建Servlet对象
0或者正数:服务器启动时创建Servlet对象 数字越小优先级越高
底层会根据loadOnStartup (从0开始)值排序,越小越优先加载创建
2. WEB容器调用Servlet的init()方法,对Servlet进行初始化
在Servlet实例化之后,Servlet容器会调用init()方法,来初始化该对象,主要是为了让Servlet对象在处理客户请求前可以完成一些初始化的工作,例如,建立数据库的连接,获取配置信息等。
对于每一个Servlet实例,init()方法只能被调用一次。init()方法有一个类型为ServletConfig的参数,Servlet容器通过这个参数向Servlet传递配置信息。Servlet使用ServletConfig对象从Web应用程序的配置信息中获取以名-值对形式提供的初始化参数。
另外,在Servlet中,还可以通过ServletConfig对象获取描述Servlet运行环境的ServletContext对象,使用该对象,Servlet可以和它的Servlet容器进行通信。无论有多少客户机访问Servlet,都不会重复执行init()。
3. Servlet初始化之后,将一直存在于容器中,service()响应客户端请求
- 如果客户端发送GET请求,容器调用Servlet的doGet方法处理并响应请求
- 如果客户端发送POST请求,容器调用Servlet的doPost方法处理并响应请求
- 或者统一用service()方法处理来响应用户请求
service()是Servlet的核心,负责响应客户的请求。
每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
要注意的是,在service()方法被容器调用之前,必须确保init()方法正确完成。容器会构造一个表示客户端请求信息的请求对象(类型为ServletRequest)和一个用于对客户端进行响应的响应对象(类型为ServletResponse)作为参数传递给service()方法。
在service()方法中,Servlet对象通过ServletRequest对象得到客户端的相关信息和请求信息,在对请求进行处理后,调用ServletResponse对象的方法设置响应信息。
4. WEB容器决定销毁Servlet时,先调用Servlet的destroy()方法,通常在关闭web应用之前销毁Servlet
destroy()仅执行一次,在服务器端停止且卸载Servlet时执行该方法。
当容器检测到一个Servlet对象应该从服务中被移除的时候,容器会调用该对象的destroy()方法,以便让Servlet对象可以释放它所使用的资源,保存数据到持久存储设备中。例如,将内存中的数据保存到数据库中,关闭数据库的连接等。
当需要释放内存或者容器关闭时,容器就会调用Servlet对象的destroy()方法。在Servlet容器调用destroy()方法前,如果还有其他的线程正在service()方法中执行,容器会等待这些线程执行完毕或等待服务器设定的超时值到达。
一旦Servlet对象的destroy()方法被调用,容器不会再把其他的请求发送给该对象。如果需要该Servlet再次为客户端服务,容器将会重新产生一个Servlet对象来处理客户端的请求。在destroy()方法调用之后,容器会释放这个Servlet对象,在随后的时间内,该对象会被Java的垃圾收集器所回收。
servlet 线程是否安全?
servlet 对象默认是单例,在jvm内存中只会存在一份,当多个线程如果共享到同一个全局变量可能会存在线程安全性问题。