文章目录
- 什么是Servlet
- 为什么需要Servlet
- 从不同角度来看Servlet
- 总过程
- Servlet之间的继承关系
- Servlet接口接口中方法
- GenericServlet抽象方法
- HttpServlet 抽象子类
- 小结
- Servlet生命周期
- 从Servlet接口方法开始
- 修改Servlet创建对象的时机
- Servlet容器
- Servlet相关的保存作用域
- request:一次请求响应范围
- session:一次会话范围有效
- application: 一次应用程序范围有效
什么是Servlet
为什么需要Servlet
Servlet名字的含义
- Servlet=Server+applet
- Server:服务器
- applet:小程序
- Servlet含义是服务器端的小程序
我们需要将一次HTTP请求-响应(不限于HTTP),对应到Java的语句(Java的语句一定放在方法中)——将请求-响应的处理过程对应到某个类下的方法对应起来——Servelet容器的功能
生活中的例子
对应的web应用
-
在整个Web应用中,Servlet主要负责处理请求、协调调度功能。我们可以把Servlet称为Web应用中的**『控制器』**
-
Servlet是Java提供的一门动态web资源开发的技术,如何称为动态,也就是不同的用户访问一个资源,所展现的页面是不一样的,比如张三访问是张三欢迎你,李四访问是李四欢迎你,我访问是你不能访问该页面,类似这种功能的实现
-
Servlet是JavaEE的规范之一,其实就是一个接口
,将来我们需要具有Servlet功能的类型来实现Servlet接口,并由web服务器来运行Servlet -
Tomcat是一个轻量级的web服务器,可以称为web容器,Servlet容器,web服务器封装了http协议,简化我们的开发,其次将web项目部署到服务器中,可以对外提供网上浏览服务
从不同角度来看Servlet
总过程
Servlet之间的继承关系
Servlet的继承关系 - 重点查看的是服务方法(service())
- javax.servlet.Servlet接口
- javax.servlet.GenericServlet抽象类
- javax.servlet.http.HttpServlet抽象子类
- javax.servlet.GenericServlet抽象类
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();
}
-
这是Servlet的源码,我们看到有
-
void init(config) - 初始化方法
-
void service(request,response) - 服务方法
-
void destory() - 销毁方法
-
GenericServlet抽象方法
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
- void service(request,response) - 我们仍然是抽象的
HttpServlet 抽象子类
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {HttpServletRequest request;HttpServletResponse response;try {request = (HttpServletRequest)req;response = (HttpServletResponse)res;} catch (ClassCastException var6) {throw new ServletException("non-HTTP request or response");}this.service(request, response);}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String method = req.getMethod();long lastModified;if (method.equals("GET")) {lastModified = this.getLastModified(req);if (lastModified == -1L) {this.doGet(req, resp);} else {long ifModifiedSince;try {ifModifiedSince = req.getDateHeader("If-Modified-Since");} catch (IllegalArgumentException var9) {ifModifiedSince = -1L;}if (ifModifiedSince < lastModified / 1000L * 1000L) {this.maybeSetLastModified(resp, lastModified);this.doGet(req, resp);} else {resp.setStatus(304);}}} else if (method.equals("HEAD")) {lastModified = this.getLastModified(req);this.maybeSetLastModified(resp, lastModified);this.doHead(req, resp);} else if (method.equals("POST")) {this.doPost(req, resp);} else if (method.equals("PUT")) {this.doPut(req, resp);} else if (method.equals("DELETE")) {this.doDelete(req, resp);} else if (method.equals("OPTIONS")) {this.doOptions(req, resp);} else if (method.equals("TRACE")) {this.doTrace(req, resp);} else {String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[]{method};errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(501, errMsg);}}
- void service(HttpServletRequest req, HttpServletResponse resp) - 不是抽象的
- String method = req.getMethod(); 这个方法是获取请求的方式
- 然后通过后面的if和else if的结构来通过什么类型的方法去执行不同的逻辑
- 决定调用的是哪个do开头的方法
- 然后通过后面的if和else if的结构来通过什么类型的方法去执行不同的逻辑
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String protocol = req.getProtocol();String msg = lStrings.getString("http.method_post_not_supported");if (protocol.endsWith("1.1")) {resp.sendError(405, msg);} else {resp.sendError(400, msg);}}
- 在HttpServlet这个抽象类中,do方法都差不多
- 那么在HttpServlet中这些do方法默认都是405的实现风格-要我们子类去实现对应的方法,否则默认会报405错误
小结
- 继承关系: HttpServlet -> GenericServlet -> Servlet
2) Servlet中的核心方法: init() , service() , destroy()
3) 服务方法: 当有请求过来时,service方法会自动响应(其实是tomcat容器调用的)
在HttpServlet中我们会去分析请求的方式:到底是get、post、head还是delete等等
然后再决定调用的是哪个do开头的方法
那么在HttpServlet中这些do方法默认都是405的实现风格-要我们子类去实现对应的方法,否则默认会报405错误
4) 因此,我们在新建Servlet时,我们才会去考虑请求方法,从而决定重写哪个do方法
Servlet生命周期
从Servlet接口方法开始
我们所谓Servlert 生命周期:从出生到死亡的过程就是生命周期。对应Servlet中的三个方法:init(),service(),destroy()
例子
//演示Servlet的生命周期
public class Demo2 extends HttpServlet {public Demo2(){System.out.println("正在实例化....");}@Overridepublic void init() throws ServletException {System.out.println("正在初始化.....");}@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("正在服务.....");}@Overridepublic void destroy() {System.out.println("正在销毁......");}
}
执行结果
//第一次访问Demo2
正在实例化....
正在初始化.....
正在服务.....
//第二次访问Demo2
正在服务.....
//第三次访问Demo3
正在服务.....
结论
默认情况下:
-
第一次接收请求时,这个Servlet会进行实例化(调用构造方法)、初始化(调用init())、然后服务(调用service())
-
从第二次请求开始,每一次都是服务
-
当容器关闭时,其中的所有的servlet实例会被销毁,调用销毁方法 destroy()
-
从调用构造方法可以看出 Servlet实例tomcat只会创建一个,所有的请求都是这个实例去响应。
-
一般情况下,第一次请求时,tomcat才会去实例化,初始化,然后再服务.这样的好处是什么? 提高系统的启动速度 。 这样的缺点是什么? 第一次请求时,耗时较长。
- 因此得出结论: 如果需要提高系统的启动速度,当前默认情况就是这样。如果需要提高响应速度,我们应该设置Servlet的初始化时机。
-
Servlet在容器中是:单例的、线程不安全的
- 单例:所有的请求都是同一个实例去响应
- 线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化
- 我们已经知道了servlet是线程不安全的,给我们的启发是: 尽量的不要在servlet中定义成员变量。如果不得不定义成员变量,那么不要去:
- ①不要去修改成员变量的值
- ②不要去根据成员变量的值做一些逻辑判断
修改Servlet创建对象的时机
<!-- 配置Servlet本身 -->
<servlet><!-- 全类名太长,给Servlet设置一个简短名称 --><servlet-name>HelloServlet</servlet-name><!-- 配置Servlet的全类名 --><servlet-class>com.atguigu.servlet.HelloServlet</servlet-class><!-- 配置Servlet启动顺序 --><load-on-startup>1</load-on-startup>
</servlet>
- 我们可以通过来设置servlet启动的先后顺序,数字越小,启动越靠前,最小值0,
- 修改启动顺序后:创建就在Web应用启动过程中
小结
名称 | 时机 | 次数 |
---|---|---|
创建对象 | 默认情况:接收到第一次请求 修改启动顺序后:Web应用启动过程中 | 一次 |
初始化操作 | 创建对象之后 | 一次 |
处理请求 | 接收到请求 | 多次 |
销毁操作 | Web应用卸载之前 | 一次 |
Servlet容器
容器
在开发使用的各种技术中,经常会有很多对象会放在容器中。
容器提供的功能
容器会管理内部对象的整个生命周期。对象在容器中才能够正常的工作,得到来自容器的全方位的支持。
- 创建对象
- 初始化
- 工作
- 清理
容器本身也是对象
- 特点1:往往是非常大的对象
- 特点2:通常的单例的
典型Servlet容器产品举例
- Tomcat
- jetty
- jboss
- Weblogic
- WebSphere
- glassfish
Servlet相关的保存作用域
- 原始情况下,保存作用域我们可以认为有四个: page(页面级别,现在几乎不用) , request(一次请求响应范围) , session(一次会话范围) ,application(整个应用程序范围)
request:一次请求响应范围
@WebServlet("/demo1")
public class Demo1 extends HttpServlet {@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1向request保存作用域保存数据request.setAttribute("uname","lili");//向demo2进行客户端重定向response.sendRedirect("demo2");//向demo2进行服务器端转发request.getRequestDispatcher("demo2").forward(request,response);}
}
@WebServlet("/demo2")
public class Demo2 extends HttpServlet {@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {Object uname = request.getAttribute("uname");System.out.println("uname="+uname);}
}
- 根据打印结果 :
- 进行客户端重定向 我们的Demo2输出null,说明获取不到name这个属性的数据
- 进行服务器转发 我们的Demo2输出了lsc,说明获得到了name这个属性的数据
session:一次会话范围有效
@WebServlet("/demo3")
public class Demo3 extends HttpServlet {@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {HttpSession session=request.getSession();session.setAttribute("uname","lili");//向demo2进行客户端重定向
// response.sendRedirect("demo2");//3.服务器端转发request.getRequestDispatcher("demo2").forward(request,response);}
@WebServlet("dem04")
public class Demo4 extends HttpServlet {@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {HttpSession session = request.getSession();System.out.println("uname"+session.getAttribute("uname"));}
}
根据打印结果 :
- 我们使用同一个客户端(打开浏览器不关闭),不管是客户端重定向还是服务器转发都能获取到name的属性值
- 如果使用的不是同一个客户端,不管是客户端重定向还是服务器转发都获取不到name的属性值
application: 一次应用程序范围有效
@WebServlet("/demo5")
public class Demo5 extends HttpServlet {@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1.向application保存作用域保存数据//ServletContext : Servlet上下文ServletContext application = request.getServletContext();application.setAttribute("uname","lili");//2.客户端重定向response.sendRedirect("demo06");//3.服务器端转发//request.getRequestDispatcher("demo04").forward(request,response);}
}
@WebServlet("/demo6")
public class Demo6 extends HttpServlet {@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//1.获取application保存作用域保存的数据,key为unameServletContext application = request.getServletContext();Object unameObj = application.getAttribute("uname");System.out.println("unameObj = " + unameObj);}
}
- 不管是不是同一个客户端,不管是转发还是重定向都能获取到数据
ServletContext : Servlet上下文
-
什么叫上下文:
- 拿生活中的例子,比如张三和李四在聊天,聊一个关于小明的八卦,他两已经聊了一会,突然王五来了说你们在聊啥,我想一起聊,王五想聊,就必须要知道张三和李四之前聊天的内容,才能加入他们一起聊小明的八卦,王五听张三和李四之前聊天的内容就是所谓的上下文
- 我们的Servlet容器是Tomcat,我们可以不严谨的称为Servlet应用程序(因为有大量的Servlet),也就是我们的Servlet上下文,我们TomCat这次启动,也就是上下文开始
-
代表:整个Web应用
-
是否单例:是
-
xxxxxxxxxx1 1request.getContextPath()java
- 获取某个资源的真实路径:getRealPath()
- 获取整个Web应用级别的初始化参数:getInitParameter()
- 作为Web应用范围的域对象
- 存入数据:setAttribute()
- 取出数据:getAttribute()