目录
前言
当我们访问网站或者使用App时,大部分场景下都是基于Browser/Server
模式,简称BS
架构,它的特点是,客户端只需要浏览器,应用程序的逻辑和数据都存储在服务器端。浏览器只需要请求服务器,获取Web
页面,并把Web
页面展示给用户即可,使用B/S
架构时,总是通过HTTP
协议实现通信。
在Web
应用中,浏览器请求一个URL
,服务器就把生成的HTML
网页发送给浏览器,而浏览器和服务器之间的传输协议是HTTP。
编写HTTP
服务器
编写HTTP
服务器需要先编写基于TCP
协议的服务,然后在一个TCP
连接中读取HTTP
请求,发送HTTP
响应即可。要编写一个完善的HTTP
服务器,以HTTP/1.1
为例,需要考虑的包括:
- 识别正确和错误的
HTTP
请求; - 识别正确和错误的
HTTP
头; - 复用
TCP
连接; - 复用线程;
IO
异常处理;
这些基础工作需要耗费大量的时间,并且经过长期测试才能稳定运行,那就根本无法做到高效而可靠地开发,因此,在JavaEE
平台上,处理TCP
连接,解析HTTP
协议这些底层工作统统扔给现成的Web
服务器去做,我们只需要把自己的应用程序跑在Web
服务器上。为了实现这一目的,JavaEE
提供了Servlet
API
,我们使用Servlet API
编写自己的Servlet
来处理HTTP
请求,Web
服务器实现Servlet
API
接口,实现底层功能。
这是一个简单的Servlet:
@WebServlet("/hello.do")
public class HelloServlet extends HttpServlet{
private HashSet<String> nameSet=new HashSet<String>();
//重写doGet方法,处理get方式的请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("哎呀,被get到了!");
}
}
一个Servlet
总是继承自HttpServlet
,然后覆写doGet()
或doPost()
方法。注意到doGet()
方法传入了HttpServletRequest
和HttpServletResponse
两个对象,分别代表HTTP
请求和响应。
Web服务器
常用的支持Servlet API的Web服务器有:
- Tomcat:由Apache开发的开源免费服务器;
- Jetty:由Eclipse开发的开源免费服务器;
- GlassFish:一个开源的全功能JavaEE服务器。
在Servlet
容器中运行的Servlet
具有如下特点:
- 无法在代码中直接通过
new
创建Servlet
实例,必须由Servlet
容器自动创建Servlet
实例; Servlet
容器只会给每个Servlet
类创建唯一实例;Servlet
容器会使用多线程执行doGet()
或doPost()
方法
一个Web App就是由一个或多个Servlet
组成的,每个Servlet
通过注解说明自己能处理的路径。一个Webapp
完全可以有多个Servlet
,分别映射不同的路径。例如:HelloServlet
能处理/hello.do
这个路径的请求。
因为浏览器发送请求的时候,还会有请求方法(HTTP Method
):即GET
、POST
、PUT
等不同类型的请求。因此,要处理GET/POST
请求,我们要覆写doGet()/doPost()
方法
当没有覆写doGet()/doPost()
方法时,会调用HttpServlet的doGet()/doPost()方法,
这时它会直接返回405
或400
错误。
Servlet生命周期
在通过一个URL
路径发起对一个Servlet
请求的过程中,其本质是在调用执行Servlet
实例的doXXX()
方法。该Servlet
实例创建和使用的过程,被称为Servlet的生命周期。整个生命周期包括:实例化、初始化、服务、销毁。
1.实例化:根据Servlet
请求的路径(例如:home.do
),查找该Servlet
的实例。如果实例不存在,则通过调用构造方法,完成Servlet
实例的创建。
//1.实例化
//构造方法
public HomeServlet() {
System.out.println("1.HomeServlet实例被创建");
}
2.初始化:通过该Servlet
的实例,调用init()
方法,执行初始化的逻辑。
//2.初始化
//重写HTTPServlet父类的init()方法
//通过实例自动调用init()方法
@Override
public void init() throws ServletException {
System.out.println("2.HomeServlet实例初始化");
}
3.服务:通过该Servlet
的实例,调用service()
方法,如果子类没有重写该方法,则调用
HttpServlet父类的service()
方法,在父类的该方法中进行请求方式的判断,如果是GET
请求,则调用doGet()
方法;如果是POST
请求,则调用doPost()
方法;
如果子类重写doXXX()
方法,则调用子类重写后的doXXX()
方法;
如果子类没有重写doXXX()
方法,则调用父类的doXXX()
方法,在父类的方法实现中,返回一个405
状态码的错误页面。
405状态码:代表请求的方式服务器不提供支持。
4.销毁:服务器关闭或重启时,会销毁所有的Servlet实例,会调用Servlet实例的destroy()
方法。
//4.销毁
@Override
public void destroy() {
System.out.println("4.HttpServlet实例被销毁");
}
servlet的生命周期顾名思义就是从servlet出现到消亡(销毁)的全过程。
主要分为以下几个阶段:
加载类—>实例化(为对象分配空间)—>初始化(为对象的属性赋值)—>请求响应(服务阶段)—>销毁