Servlet-我们到底能走多远系列(7)
我们到底能走多远系列(7)
祝大家长假快乐,工作的时候拼命,休息享受的时候也请拼命。
今天不扯淡了,过完假期再来和你们扯,哈哈!
Servlet的核心类图如下:
上面的servlet只是接口,相当于描述了servlet的标准,也就是说与协议无关。而HttpServlet的实现是根据Http协议来完成的。看下面的截图:
打开源码看后发现大多数接口和抽象类,那么真正的实现都在哪呢?在servlet容器源代码里,即类似Tomcat这样的servlet容器。
所以我觉得可以这样理解Servlet的作用:它提供了操控的按钮,这些按钮一旦被安装到Servlet容器里面,我们就可以通过这些按钮操控servlet容器,从而管理我们自己的Servlet实例。
这一点很重要。所以阅读Servlet源码其实就是了解他的基本结构和API,我们真正要读的是Tomcat的源码!
Servlet的运行结构和API:
首先看下一次Http请求-响应的过程:
A,首先客户端通过浏览器向服务器发送Http请求;
B,Tomcat监听服务器的8080端口,当有Http请求发过来之后,解析出项目名称,然后到webapps目录下搜索到该项目文件夹。
C,Tomcat作为servlet的容器,实例化第一次请求调用的的servlet实例(以后再有相同的servlet的请求,使用第一次的实例)。
D,调用init()方法,初始化工作。
E,调用HttpServlet的service方法,中间会更加请求,调用doGet或doPost方法,执行核心逻辑代码。
F,执行完servlet后,返回响应,客户端浏览器根据响应呈现效果。
然后我们看下基本的运行机制:
从图上可以看出,不同的用户请求并发使用同一个Servlet实例,这是一个比较重要的基础机制。
Container解析Http请求后,实例化Servlet,因为是Http请求,会执行HttpServlet的代码:
首先会执行HttpServlet中的service(ServletRequest req, ServletResponse res)方法:
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest) req;//请求转换成Http请求 response = (HttpServletResponse) res;//响应转换成Http响应 } catch (ClassCastException e) { throw new ServletException("non-HTTP request or response"); } service(request, response);//调用处理Http的service方法 }
然后会调用service(HttpServletRequest req, HttpServletResponse resp)方法:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod();//取得req中的方法类型 if (method.equals(METHOD_GET)) {//根据方法类型来决定执行后续的方法,我们只关注doGet和doPost long lastModified = getLastModified(req); if (lastModified == -1) { doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); if (ifModifiedSince < (lastModified / 1000 * 1000)) { maybeSetLastModified(resp, lastModified); doGet(req, resp);//如此我们就能执行到自己写的doGet方法,从而执行自己的业务逻辑。 } 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 {//不属于http的方法类型,返回501错误 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); } }
我们自己实现的Servlet,我们只要继承HttpServlet,实现doGet就ok了:
public class HelloWorld extends HttpServlet{ private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("test"); getServletContext().getRequestDispatcher("/jsp/test.html").forward(req, resp); } }
来关注下Servlet运行时需要注意的:
A,Servlet生命周期:
servlet类加载--->实例化--->服务--->销毁
B,其中牵涉到3个方法代表了Servlet的生命周期:
1、init方法:负责初始化Servlet对象。
2、service方法:负责响应客户的请求。
3、destroy方法:当Servlet对象退出生命周期时,负责释放占用的资源。
C,执行自己Servlet时需要用到的几个重要参数:
1,HttpSession:一次连结到客户端关闭,从客户端连接的层面来管理。
2,ServletConfig:从一个servlet被实例化后,对任何客户端在任何时候访问有效,但仅对本servlet
有效,一个servlet的ServletConfig对象不能被另一个servlet访问。 从单个servlet的层面来管理。
3,ServletContext:对任何servlet,任何人在任何时间都有效,这才是真正全局的对象。从全部的servlet的层面来管理
4,HttpServletRequest,HttpServletResponse:从单个servlet的单次响应侧面来管理
D,API
HttpServletRequest:
String getAuthType() 如果servlet由一个鉴定方案所保护,如HTTP基本鉴定,则返回方案名称。 String getContextPath() 返回指定servlet上下文(web应用)的URL的前缀。 Cookie[] getCookies() 返回与请求相关cookie的一个数组。 Long getDateHeader(String name) 将输出转换成适合构建Date对象的long类型取值的getHeader()的简化版。 String getHeader(String name) 返回指定的HTTP头标指。如果其由请求给出,则名字应为大小写不敏感。 Enumeration getHeaderNames() 返回请求给出的所有HTTP头标名称的权举值。 Enumeration getHeaders(String name) 返回请求给出的指定类型的所有HTTP头标的名称的枚举值,它对具有多取值的头标非常有用。 int getIntHeader(String name) 将输出转换为int取值的getHeader()的简化版。 String getMethod() 返回HTTP请求方法(例如GET、POST等等) String getPathInfo() 返回在URL中指定的任意附加路径信息。 String getPathTranslated() 返回在URL中指定的任意附加路径信息,被子转换成一个实际路径。 String getQueryString() 返回查询字符串,即URL中?后面的部份。 String getRemoteUser() 如果用户通过鉴定,返回远程用户名,否则为null。 String getRequestedSessionId() 返回客户端的会话ID String getRequestURI() 返回URL中一部分,从“/”开始,包括上下文,但不包括任意查询字符串。 String getServletPath() 返回请求URI上下文后的子串 HttpSession getSession() 调用getSession(true)的简化版。 HttpSession getSession(boolean create) 返回当前HTTP会话,如果不存在,则创建一个新的会话,create参数为true。 Principal getPrincipal() 如果用户通过鉴定,返回代表当前用户的java.security.Principal对象,否则为null。 boolean isRequestedSessionIdFromCookie() 如果请求的会话ID由一个Cookie对象提供,则返回true,否则为false。 boolean isRequestedSessionIdFromURL() 如果请求的会话ID在请求URL中解码,返回true,否则为false boolean isRequestedSessionIdValid() 如果客户端返回的会话ID仍然有效,则返回true。 Boolean isUserInRole(String role) 如果当前已通过鉴定用户与指定角色相关,则返回true,如果不是或用户未通过鉴定,则返回false。
HttpServletResponse:
void addCookie(Cookie cookie) 将一个Set-Cookie头标加入到响应。 void addDateHeader(String name,long date) 使用指定日期值加入带有指定名字(或代换所有此名字头标)的响应头标的方法。 void setHeader(String name,String value) 设置具有指定名字和取值的一个响应头标。 void addIntHeader(String name,int value) 使用指定整型值加入带有指定名字的响应头标(或代换此名字的所有头标)。 boolean containsHeader(String name) 如果响应已包含此名字的头标,则返回true。 String encodeRedirectURL(String url) 如果客户端不知道接受cookid,则向URL加入会话ID。第一种形式只对在sendRedirect()中使用的URL进行调用。其他被编码的 URLs应被传递到encodeURL() String encodeURL(String url) void sendError(int status) 设置响应状态码为指定值(可选的状态信息)。HttpServleetResponse定义了一个完整的整数常量集合表示有效状态值。 void sendError(int status,String msg) void setStatus(int status) 设置响应状态码为指定指。只应用于不产生错误的响应,而错误响应使用sendError()。
ServletContext:
Object getAttribute(String name) 返回servlet上下文中具有指定名字的对象,或使用已指定名捆绑一个对象。从Web应用的标准观点看,这样的对象是全局对象,因为它们可以被同一 servlet在另一时刻访问。或上下文中任意其他servlet访问。 void setAttribute(String name,Object obj) 设置servlet上下文中具有指定名字的对象。 Enumeration getAttributeNames() 返回保存在servlet上下文中所有属性名字的枚举。 ServletContext getContext(String uripath) 返回映射到另一URL的servlet上下文。在同一服务器中URL必须是以“/”开头的绝对路径。 String getInitParameter(String name) 返回指定上下文范围的初始化参数值。此方法与ServletConfig方法名称不一样,后者只应用于已编码的指定servlet。此方法应用于上下文中所有的参数。 Enumeration getInitParameterNames() 返回(可能为空)指定上下文范围的初始化参数值名字的枚举值。 int getMajorVersion() 返回此上下文中支持servlet API级别的最大和最小版本号。 int getMinorVersion() String getMimeType(String fileName) 返回指定文件名的MIME类型。典型情况是基于文件扩展名,而不是文件本身的内容(它可以不必存在)。如果MIME类型未知,可以返回null。 RequestDispatcher getNameDispatcher(String name) 返回具有指定名字或路径的servlet或JSP的RequestDispatcher。如果不能创建RequestDispatch,返回null。如果指定路径,必须心“/”开头,并且是相对于servlet上下文的顶部。 RequestDispatcher getNameDispatcher(String path) String getRealPath(String path) 给定一个URI,返回文件系统中URI对应的绝对路径。如果不能进行映射,返回null。 URL getResource(String path) 返回相对于servlet上下文或读取URL的输入流的指定绝对路径相对应的URL,如果资源不存在则返回null。 InputStream getResourceAsStream(String path) String getServerInfo() 返顺servlet引擎的名称和版本号。 void log(String message) void log(String message,Throwable t) 将一个消息写入servlet注册,如果给出Throwable参数,则包含栈轨迹。 void removeAttribute(String name) 从servlet上下文中删除指定属性。
HttpSession:
Object getAttribute(String name) 将会话中一个对象保存为指定名字,返回或删除前面保存的此名称对象。 void setAttribute(String name,Object value) void removeAttribute(String name) Enumeration getAttributeName() 返回捆绑到当前会话的所有属性名的枚举值。 long getCreationTime() 返回表示会话创建和最后访问日期和时间的一个长整型,该整型形式为java.util.Date()构造器中使用的形式。 long getLastAccessedTime() String getId() 返回会话ID,servlet引擎设置的一个唯一关键字。 ing getMaxInactiveInterval() 如果没有与客户端发生交互,设置和返回会话存活的最大秒数。 void setMasInactiveInterval(int seconds) void invalidate() 使得会话被终止,释放其中任意对象。 boolean isNew() 如果客户端仍未加入到会话,返回true。当会话首次被创建,会话ID被传入客户端,但客户端仍未进行包含此会话ID的第二次请示时,返回true。
ServletConfig:
getServletName()方法概述:public java.lang.String getServletName() 该方法返回一个servlet实例的名称,该名称由服务器管理员提供。 getServletContext()方法概述:public ServletContext getServletContext() 返回一个ServletContext对象的引用。 getInitParameter()方法概述:public java.lang.String getInitParameter(java.lang.String name) 返回一个由参数String name决定的初始化变量的值,如果该变量不存在,返回null。 getInitParameterNames()方法概述:public java.util.Enumeration getInitParameterNames() 返回一个存储所有初始化变量的枚举函数。如果servlet没有初始化变量,返回一个空枚举函数。
除去以上的,还有很多内容会在实际编码中需要学习。
总结:
最终我们还是不得不去阅读Tomcat的源码,才能解答更多的疑惑。
----------------------------------------------------------------------
努力不一定成功,但不努力肯定不会成功。
共勉