#深入Servlet
1. servlet类中静态代码块、构造方法、代码块、init()、destroy()方法执行顺序
1.1. 举例:
public class MyServlet extends HttpServlet {
static {
System.out.println("静态代码块被执行了");
}
public MyServlet() {
System.out.println("构造方法被执行了");
}
{
System.out.println("代码块被执行了");
}
public void init() throws ServletException {
System.out.println("init() 方法被执行了");
}
public void destroy() throws ServletException {
System.out.println("destroy() 方法被执行了");
}
// 省略其他方法
}
当 Servlet 容器启动时,它会加载 MyServlet 类并创建它的实例。在这个过程中,上述四个部分的代码将按照以下顺序执行:
- 在类加载时,静态代码块会被执行一次。在容器加载 MyServlet 类时被执行。
- 在构造方法被执行前,代码块会被执行一次。
- 当 MyServlet 实例被创建时,它的构造方法会被执行。会在容器创建 MyServlet 实例时被执行。
- 在 MyServlet 实例被创建完成后,Servlet 容器会调用它的 init() 方法,完成 Servlet 的初始化工作。会在容器创建 MyServlet 实例并调用它的 init() 方法时被执行。
- 在服务器正常停止后Servlet容器会调用它的 destroy() 方法,完成 Servlet 的结束工作。
2. 客户(浏览器)发起http访问http://localhost:8080/web0327/hello服务端做了什么事情。
- 服务器开启(监听)
- 客户端发起http://localhost:8080/web0327/hello访问
- tomcat服务器接收到,对应的是web0327项目提供服务,通过web0327项目中web.xml (DD)知道对应 url-pattern , HelloServlet提供服务。
0. 创建ServletContext对象,读取DD中的context-param信息
1. 创建HelloServlet对象 new HelloServlet()
2. 调用init方法
3. 创建HelloServlet对应的ServletConfig对象 (读取init-param信息)
4. 开启新的线程,调用Servlet接口中定义的service()
5. 找到HttpServlet中定义的service方法。
6. service(HttpServletRequest request, HttpServletResponse response)
7. 创建HttpServletRequest request, HttpServletResponse response,把这两个对象,传入到service方法中
8. HttpServlet中的service方法,根据请求的方式,相应调用doGet()
9. 最后被调用的是:HelloServlet类中定义的doGet(HttpServletRequest request, HttpServletResponse response)
10. 开发者而言,只要在doGet(HttpServletRequest request, HttpServletResponse response)中,对request, response操作
11. 操作后的结果,返回给浏览器
-
HelloServlet对象,是“单例”多线程的。
所有的访问,http://localhost:8080/web0327/hello
tomcat都会开启一个“新线程”让HelloServlet对象,
并且,为这次访问,创建HttpServletRequest request, HttpServletResponse response对象,调用
service(HttpServletRequest request, HttpServletResponse response)把2个对象传递。
当这次请求结束了,这个线程也释放,HttpServletRequest request, HttpServletResponse response存在堆上,
通过线程上的service方法栈上进行引用。这个两个对象,随着请求的结束而被放弃引用(销毁) -
N次http访问,到HelloServlet,
请问HelloServlet对象有几个? 始终1个“单例”
HttpServletRequest和HttpServletResponse对象,有几个? N,tomcat会为每次访问,创建request和response对象 -
解释转发(请求分派)
request.getRequestDispatcher(url).forward(request, response); 相当于这次访问没有结束,而是调用url的资源,把request, response传递过去重定向(再次发起新的请求)
response.sendDirect(url); 说明本次请求结束了。提醒浏览器再次发起新的访问url
所以,就再次发起新的请求到url, tomcat重新为这次请求开启新线程,创建request, response对象!
3. 简述request获取请求参数的几种方式
- URL查询参数(Query Parameters):可以通过ServletRequest对象的getQueryString()方法获取URL中的查询参数,也可以通过ServletRequest对象的getParameter()方法获取指定查询参数的值。例如:
String queryString = request.getQueryString();
String paramValue = request.getParameter("paramName");
- 请求体中的表单(Form Data):可以通过ServletRequest对象的getParameter()方法获取表单参数的值,也可以通过ServletRequest对象的getParameterValues()方法获取多个表单参数的值。例如:
String paramValue = request.getParameter("paramName");
String[] paramValues = request.getParameterValues("paramName");
- 请求体中的JSON数据:可以通过ServletRequest对象的getReader()方法获取请求体的字符流,然后使用第三方JSON库解析JSON数据。例如:
BufferedReader reader = request.getReader();
JsonObject json = new Gson().fromJson(reader, JsonObject.class);
- 请求头中的参数(Header Parameters):可以通过ServletRequest对象的getHeader()方法获取指定请求头的值,也可以通过ServletRequest对象的getHeaderNames()方法获取所有请求头的名称。例如:
String headerValue = request.getHeader("headerName");
Enumeration<String> headerNames = request.getHeaderNames();
- 请求体中的文件(File Uploads):可以使用第三方文件上传组件(如Apache Commons FileUpload)来处理文件上传,并通过ServletRequest对象的getPart()方法获取上传文件的Part对象。例如:
ServletFileUpload fileUpload = new ServletFileUpload();
List<FileItem> items = fileUpload.parseRequest(request);
for (FileItem item : items) {
if (!item.isFormField()) {
Part filePart = request.getPart(item.getFieldName());
}
}
- 请求体中的原始数据(Raw Data):如果请求体中的数据不是表单数据或JSON数据,而是原始数据(例如XML或文本数据),可以通过ServletRequest对象的getReader()方法或getInputStream()方法获取请求体的字符流或字节流。例如:
BufferedReader reader = request.getReader();
InputStream inputStream = request.getInputStream();
需要注意的是,在使用getReader()方法获取字符流之后,就不能再使用getInputStream()方法获取字节流了,因为这两种方法只能获取一次请求体中的数据。