关于Servlet
Servlet与CGI
CGI(Common Gateway Interface),早期的Web服务器技术。执行模式:将服务端的资源基于进程运行。
Servlet:运行模式改为单进程多线程的形式,利用容器管理。
由于进程的执行非常耗费时间,且存在空间浪费,因此效率相比Servlet而言较为低下。
Servlet的生命周期
Servlet的生命周期是由容器管理的。经历三个时间段:初始化、服务、销毁。
1.当客户端初次请求服务器访问某个Servlet时,容器会将这个Servlet实例化,并调用init方法。
2.然后在新的线程中调用Service方法。此时,执行玩Service方法并不会立即销毁。
3.在容器关闭时,会调用destroy方法,然后销毁该对象。
注:若在销毁前有客户端访问该Servlet,不会实例化新的Servlet对象,而是直接启动新的线程调用Service方法。
Servlet的执行过程
1.客户端请求某个Servlet,产生对应的协议包头(以Http协议为例)。
2.服务器解析Http协议,根据解析后的信息,从Web.xml找到的处理映射(地址-Servlet类)。
3.通过Service函数执行产生新的线程,Service函数根据协议决定调用doPost还是goGet方法或其他函数进行数据处理。
4.将响应的数据封装成Http包,响应给客户端。
Servlet的线程安全问题
Servlet由于使用到多线程,而在JVM内存模型中,所有实例化的对象都被分配在堆区,而堆是所有线程共享的,所以若在对应的处理方法中实例化了对象,就会发生线程安全问题。
解决方法:
1.避免使用实例变量,必要使用时,使用同步资源监视器或锁实现数据安全。
2.实现SingleThreadMode接口,实现了该接口,那么Tomcat将保证在一个时刻仅有一个线程可以在给定的Serlvet实例的service方法中执行。其他请求进行排队,由于效率低下,已被废弃。
Servlet的属性线程安全问题
ServletContext:它是线程不安全的,多线程下可以同时进行读写,因此我们要对其读写操作进行同步或者深度的clone。
HttpSession:同样是线程不安全的,和ServletContext的操作一样。
ServletRequest:它是线程安全的,对于每一个请求由一个工作线程来执行,都会创建一个ServletRequest对象,所以ServletResquest只能在一个线程中被访问,而且他只在service()方法内是有效的。
Servlet体系结构
其中HttpServlet类是自定义Servlet的父类,父类函数中包含Service函数,HttpServlet中的Service函数决定了要调用的处理函数。
@Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try {
request = (HttpServletRequest) req; response = (HttpServletResponse) res; } catch (ClassCastException e) { throw new ServletException("non-HTTP request or response"); } service(request, response); } protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); } catch (IllegalArgumentException iae) { // Invalid date header - proceed as if none was set ifModifiedSince = -1; } if (ifModifiedSince < (lastModified / 1000 * 1000)) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } }