由官方定义的一系列借口,如Tmocat,最后交给Web服务器来运行我们编写的Servlet。
不再是直接由Tomcat服务器发送编写好的静态网页内容如HTML文件,而是由我们通过JAVA代码进行拼接,更好地实现动态网页。
注册Servlet
- 这里的test是访问路径,我们HTTP协议URL中最后就是端口或者路径。
1 |
|
我们不仅可以用@WebServlet来进行注册,还可以用web.xml中进行注册。
1 | <servlet> |
不过现在都是用@WebServlet来进行的。
此时还有一个问题,为什么我们之前没有写这个但是Tomcat服务器可以运行静态HTML呢?因为它其实有一个默认的Servlet类,其实是通过那个了。
Servlet的生命周期
服务器开启时:
- 构造方法
init初始化方法service方法(如果浏览器请求了两次,是因为有一次请求的是favicon.ico)- 之后就一直用这个对象,就导致了我们之后再进行请求还是调用service方法,并不会再进行构造方法和初始化方法。
- 关闭服务器后会
destroy方法 - 对象被回收
实际上我们再Web应用程序运行时,每当浏览器向服务器发送一个请求时,都会创建一个线程执行一次service方法,让我们处理多用户的请求,并将结果响应给用户。
而在service方法中有两个参数,这两个都是接口,Tomcat服务器自动给我们传入了实现类: - ServletRequest:请求,HTTP请求文中的所有内容都可以从中获取
- ServletResponse:返回给浏览器的HTTP响应报文的实体类封装
不过ServletRequest这个接口本身的方法很少,有一个子接口HttpServletRequest继承于他,并且Tomcat的实现类实现了这个接口,也就说里面方法更多:
1 |
|
同理ServletRequest有也更多方法的ServletResponse:
1 | //转换为HttpServletResponse(同上) |
我们可以在service方法中用该接口实行更多命令。

- 容器 = 房子
- 实例 = 住在房子里的人
① 客户端 → Web 服务器(Tomcat):发送 HTTP 请求
-
客户端(浏览器)输入网址,发送HTTP 请求
-
请求先到 Tomcat(Web 服务器)
-
Tomcat 里的Servlet 容器接收请求,做 2 件事:
- 封装请求数据 → 生成
ServletRequest(request 对象),传给你的 Servlet - 提前创建好
ServletResponse(response 对象),传给你的 Servlet
- 封装请求数据 → 生成
写的代码:
response.setHeader("Content-type", "text/html;charset=UTF-8");就是在Servlet 实例里,操作这个提前创建好的
response,设置响应头、编码。
Servlet 实例处理请求
- Servlet 拿到
request,获取浏览器传的参数 - Servlet 通过
response设置响应头、编码、输出网页内容 - 处理完后,把要返回的数据交给
response对象
② Tomcat → 客户端:返回 HTTP 响应
- Servlet 容器读取
response里的内容(包括你设置的Content-type、HTML 文本) - Tomcat 把这些数据包装成标准 HTTP 响应
- 发回浏览器,浏览器根据
Content-type:html;UTF-8解析网页、显示中文
解读和使用HttpServlet
我们注册的Servlet实例其实就是那些实现了Servlet接口的实现类对象,其实已经有内置的Servlet接口实现类了,比如GenericServlet类。这个类完善了很多方法,但是没有完善service方法。可还有一个类继承于GenericServlet,也就是HttpServlet,它完善了service方法。
1 |
|
为什么我们这里要重写doGet,是因为HttpServlet中的service方法最终会进入doGet方法。而默认的doGet方法是会返回错误的方法405的,但是他把HttpServletRequest req, HttpServletResponse resp也一并传了进来,并且是service方法的末端,所以我们这里只需要编写doGet方法就好了。
但是当然不只会进入goGet方法,他其实是有一个请求头判断的,判断你用什么方法就进什么方法里面。不过这里还得重写其他东西,以后用了再说。
@WebServlet注解
路径
1 | ; test路径才能访问这个Servlet容器 |
第二种用法
1 | ; 不管什么html文件都能访问这个Servlet容器 |
/呢?
1 | ; 替换掉了默认的Servlet容器了 |
多个访问路径
1 | ; 多个地址匹配 |
预加载
1 | ; |
Servlet上传和下载文件
下载文件
首先我们来看看比较简单的下载文件,首先将我们的icon.png放入到resource文件夹中,接着我们编写一个Servlet用于处理文件下载:
1 |
|
为了更方便写JavaIO,我们可以用这个依赖:
1 | <dependency> |
补全:
1 | resp.setContentType("image/png"); |
接着我们需要在前端写上一个下载链接:
1 | <hr> |
这里的file是路径,而download是返回的文件名字。
后端设置了 Content-Type + 前端 <a>加了 download 属性,浏览器收到字节流后,就不会直接展示,而是自动转为下载行为。否则会直接渲染这张图片在浏览器。
上传文件
先来编写前端:
1 | <form method="post" action="file" enctype="multipart/form-data"> |
注意必须添加enctype="multipart/form-data",来表示此表单用于文件传输。
接着来添加Post方法:
1 |
|
注意,必须添加@MultipartConfig注解来表示此Servlet用于处理文件上传请求。
前端XHR请求
我们希望,网页中的部分内容,可以动态显示,比如网页上有一个时间,旁边有一个按钮,点击按钮就可以刷新当前时间。
这个时候我们就需要网页展示的同时给后端发送请求,并根据后台响应,动态更新页面。在Js前端中有一个东西叫做XMLHttpRequest请求,这个东西可以发送给后端的一些Servlet容器,从而获得一些响应头和响应体。
1 | function updateTime() { |
接着修改一下前端页面,添加一个时间显示区域:
1 | <hr> |
最后创建一个Servlet用于处理时间更新请求:
1 |
|
重定向与请求转发
重定向
我们要求用户登陆后就可以跳转到首页,这个时候就可以用重定向来完成。
我们其实只需要在登陆成功后添加一行代码就好:
1 | resp.sendRedirect("time"); |
调用后,响应的状态码会被设置为302,并且响应头中添加了一个Location属性,此属性表示,需要重定向到哪一个网址。
请求转发
接着来看下请求转发,请求转发其实是一种服务器内部的跳转机制,是将我们第一次的请求转发给了另一个Servlet,然后另一个Servlet来处理:
1 | req.getRequestDispatcher("/time").forward(req, resp); |
比如这个就是交给了time这个Servlet来处理,而这种情况下其实是只请求了一次。可是重定向不同,它是我们浏览器又请求了一次。
这里要注意一下,我们请求转发的话,请求体是什么方法,在新的Servlet中也是会跳转到这个方法,记得重写下。
携带数据
请求转发是可以携带数据过去的,而重定向不可以。
1 | req.setAttribute("test", "我是请求转发前的数据"); |
- 请求转发是一次请求,重定向是两次请求
- 请求转发地址栏不会发生改变, 重定向地址栏会发生改变
- 请求转发可以共享请求参数 ,重定向之后,就获取不了共享参数了
- 请求转发只能转发给内部的Servlet
了解ServletContext对象
ServletContext全局唯一,它是属于整个Web应用程序的,我们可以通过getServletContext()来获取到此对象。
我们可以给它加属性和参数:
1 | ServletContext context = getServletContext(); |
接着我们去另一个Servlet:
1 | System.out.println(getServletContext().getAttribute("test")); |
我们发现无论在哪个Servlet中我们获得到的ServletContext都是同一个。还可以用作请求转发:
1 | context.getRequestDispatcher("/time").forward(req, resp); |
它还可以获取根目录下的资源文件(注意是webapp根目录下的,不是resource中的资源)
初始化参数
我们每个Servlet容器在刚开始的时候可以定义一些本Servlet的初始化参数:
1 |
它也是以键值对形式保存的,我们可以直接通过Servlet的getInitParameter方法获取:
1 | System.out.println(getInitParameter("test")); |
但就像我所说,只适用于本Servlet。我们当然也可以定义全局的,不过得去web.xml当中:
1 | <context-param> |
我们需要使用ServletContext来读取全局初始化参数:
1 | ServletContext context = getServletContext(); |
说些什么吧!