两个月前学习的Servlet的源码,一直放在草稿箱里。之所以看Servlet源码,是因为Spring源码太难了,又臭又长。接下来先来介绍源码都有哪些类和接口,servlet的源码主要是在javax.servlet和javax.servlet.http两个包下面,总共有34个左右的类和接口,下图是核心的类与接口之间的关系,其中有的类和接口中的方法太多,由于写出来太过于冗余,我只写了部分,下图只作记忆
在按流程读源码之前,我们先看Servlet中比较重要的类和接口的定义及概念,方便之后回头再看:
浏览器请求:
1、首先由客户端浏览器请求访问路径URL,向服务器发送一个HTTP请求,这个请求首先会到达Tomcat内置Web服务器
2、Tomcat内置Web服务器接收到请求后,会将请求发送到Servlet容器中
3、Servlet容器接受到请求后会加载Servlet,在web.xml中根据映射地址找到相应的servlet名,然后根据servlet名找到我们全限定类名,既我们自己写的类。
服务器创建对象:
服务器找到权限类名后,通过反射创建对象,同时创建了ServletConfig,里面也存放了一些初始化信息(注意服务器只会创建一次servlet对象,所以servlet也只会有一个)
调用init方法:
1、对象创建好之后,首先要执行init方法,但是我们发现我们自定义类下面没有init方法,所以程序会到其父类HttpServlet里面找(创建一个Servlet实例,我们一般都是继承HttpServlet,当然也可以继承GenericServlet)
2、我们发现HttpServlet里也没有init方法,所以继续向上找,既向其父类GenericServlet中继续寻找,在GenericServlet中,我们发现了init方法,则执行init方法(对接口Servlet中的init方法进行了重写)
注:在GenericServlet中执行public void init(ServletConfig config)方法的时候,又调用了自己无参方法体的init()方法,其目的是为了方便开发者,如果开发者在初始化的过程中需要实现一些功能,可以重写此方法
调用service方法:
1、接着服务器会自动默认的执行service方法,在你写的类中找service方法,但是找不到,那么会到其父类中寻找
2、到父类HttpServlet中发现有此方法,则直接调用此方法,发现方法依赖于两个参数,所以先去创建两个对象:ServletRequest请求对象和ServletResponse响应的对象,这两个对象用来封装浏览器的请求数据和封装向浏览器的响应数据,接着将这两个参数传入,并将这两个参数进行强制类型转换,随后调用HttpServlet下面另外的一个publci void service(HttpServletRequest req,HttpServletResponse resp),在这个方法体内进行了请求的判断,根据ServletRequest中获得客户的请求信息,然后调用相应的doXXX方法进行响应,再通过ServletResponse对象生成响应结果,发送给客户端,最后销毁创建的ServletRequest和ServletResponse
调用destory()
在源码中,GenericServlet这个类里面实现了父接口Servlet的方法destory(),但是没有方法体,我就很纳闷,没有方法体怎么销毁servlet对象呢?
原来这个方法只是起到一个通知的作用,手动调用这个方法不会销毁servlet实例对象,只有在web应用关闭终止时,servlet才会被销毁。服务器关闭时,调用这些servlet对象的destory()方法,通知这些对象,我要销毁你们了,当然还会销毁servlet创建的servletConfig对象。
servlet接口是servlet源码中最高级的接口,接口中定义了servlet的初始化,处理请求和销毁
public interface Servlet {
/**
* 负责初始化Servlet对象
* 在Servlet的生命周期中,该方法执行一次
* 该方法执行在单线程的环境下,因此开发者不用考虑线程安全呢的问题
*/
void init(ServletConfig var1) throws ServletException;
/**
* 返回Servlet配置和初始化参数
*/
ServletConfig getServletConfig();
/**
* 负责响应客户的请求
* 为了提高效率,Servlet规范要求一个Servlet实例必须能够同时服务于多个客户端请求
* 即 service() 方法运行在多线程的环境下,Servlet 开发者必须保证该方法的线程安全性
*/
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
/**
* 返回有关servlet的信息,例如作者、版本、版权
*/
String getServletInfo();
/**
* 当Servlet对象退出生命周期时,负责释放占用的资源
*/
void destroy();
}
其中的init(ServletConfig congif)方法和destory()有点像构造函数和析构函数,从service()方法中的 参数可以看出,其方法依赖于ServletRequest和和ServletResponse两个相对高级的接口。
在应用初始化的时候,Web容器在创建Servlet对象时会自动将web.xml中的servlet配置这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给Servlet,所以我们可以通过ServletConfig对象就可以的到当前servlet的初始化参数信息,所以说,ServletConfig是容器向servlet传递参数的载体
public interface ServletConfig {
/**
* 返回此servlet实例的名称
* 在web.xml对应于servlet-name 结点( )
* 使用注解@WebServlet(value="/hello",name="HelloServlet")中的name属性
*/
String getServletName();
//返回这个Servlet的ServletContext对象
ServletContext getServletContext();
/**获取具有给定名称的初始化参数的值
* 在web.xml对应于
*/
String getInitParameter(String var1);
//返回指定的参数的参数名
Enumeration getInitParameterNames();
}
从方法中可以看出ServletConfig又依赖于ServletContext这个接口
GenericServlet实现了Servlet和ServletConfig里的所有方法,并且额外提供了一个初始化的方法inti()//实现为空和两个打印日志的方法log(String msg) 与 log(String message,Throwable t),当然Generic也实现了Servlet中的个别方法
GenericServlet中的一个service()方法需要我们实现
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
private static final long serialVersionUID = 1L;
/**
* 也需要一个ServletConfig,和HttpServlet相似
*/
private transient ServletConfig config;
public GenericServlet() {
}
public void destroy() {
}
public String getInitParameter(String name) {
return this.getServletConfig().getInitParameter(name);
}
public Enumeration getInitParameterNames() {
return this.getServletConfig().getInitParameterNames();
}
public ServletConfig getServletConfig() {
return this.config;
}
public ServletContext getServletContext() {
return this.getServletConfig().getServletContext();
}
public String getServletInfo() {
return "";
}
/**
* 实例化Servlet中的init()方法
*/
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
/**
* 提供了一个空构造
*/
public void init() throws ServletException {
}
public void log(String msg) {
this.getServletContext().log(this.getServletName() + ": " + msg);
}
public void log(String message, Throwable t) {
this.getServletContext().log(this.getServletName() + ": " + message, t);
}
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
public String getServletName() {
return this.config.getServletName();
}
}
GenericServlet中的大部分操作都依赖于ServletConfig,如其下:
private transient ServletConfig config;
Tomcat在启动的时候,需要识别webapps下的各个WEB应用,识别各个WEB应用的同时为每个WEB应用创建对应的ServletContext对象,一个WEB应用对应于一个ServletContext对象,一个ServletContext对象表示了一个Web应用程序的上下文,也就是Servlet的上下文,每个ServletContext中都有一个很大的MAP,并且Tomcat在启动之初就向这个MAP中放入了大量的键值对数据,servletContext也称作是一个域对象,服务器启动的时候创建,服务器关闭的时候销毁
public interface ServletContext {
/**
* 代码略
*/
//获取ServletContext对象
ServletContext getContext(String var1);
String getContextPath();
Set getResourcePaths(String var1);
URL getResource(String var1) throws MalformedURLException;
//获取服务端指定目录下指定文件的输入流对象
InputStream getResourceAsStream(String var1);
RequestDispatcher getRequestDispatcher(String var1);
RequestDispatcher getNamedDispatcher(String var1);
/** @deprecated */
Servlet getServlet(String var1) throws ServletException;
/** @deprecated */
Enumeration getServlets();
/** @deprecated */
Enumeration getServletNames();
void log(String var1);
/** @deprecated */
void log(Exception var1, String var2);
void log(String var1, Throwable var2);
//获取服务端指定目录下指定资源/目录的真实路径
String getRealPath(String var1);
/**
* servletContext读取全局参数核心方法如下(获取的是web.xml里设置好的参数):
*/
String getServerInfo();
//根据指定的参数名获取参数值
String getInitParameter(String var1);
//获取所有参数名称列表
Enumeration getInitParameterNames();
boolean setInitParameter(String var1, String var2);
//根据指定的key读取ServletContext域对象里面的数据
Object getAttribute(String var1);
//向ServletContext域对象中添加数据,添加时以Key-value形式添加
void setAttribute(String var1, Object var2);
//根据指定的key从ServletContext域对象中删除数据
void removeAttribute(String var1);
String getServletContextName();
Dynamic addServlet(String var1, String var2);
Dynamic addServlet(String var1, Servlet var2);
Dynamic addServlet(String var1, Class extends Servlet> var2);
T createServlet(Class var1) throws ServletException;
javax.servlet.FilterRegistration.Dynamic addFilter(String var1, String var2);
javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Filter var2);
javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Class extends Filter> var2);
T createFilter(Class var1) throws ServletException;
void addListener(String var1);
void addListener(T var1);
void addListener(Class extends EventListener> var1);
ClassLoader getClassLoader();
}
public abstract class HttpServlet extends GenericServlet {
//定义了一些字符串常量
public HttpServlet() {
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_put_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
} catch (ClassCastException var6) {
throw new ServletException("non-HTTP request or response");
}
this.service(request, response);
}
}
public interface ServletRequest {
Object getAttribute(String var1);
Enumeration getAttributeNames();
String getCharacterEncoding();
void setCharacterEncoding(String var1) throws UnsupportedEncodingException;
int getContentLength();
String getContentType();
ServletInputStream getInputStream() throws IOException;
String getParameter(String var1);
Enumeration getParameterNames();
String[] getParameterValues(String var1);
Map getParameterMap();
String getProtocol();
String getScheme();
String getServerName();
int getServerPort();
BufferedReader getReader() throws IOException;
String getRemoteAddr();
String getRemoteHost();
void setAttribute(String var1, Object var2);
void removeAttribute(String var1);
Locale getLocale();
Enumeration getLocales();
boolean isSecure();
RequestDispatcher getRequestDispatcher(String var1);
/** @deprecated */
String getRealPath(String var1);
int getRemotePort();
String getLocalName();
String getLocalAddr();
int getLocalPort();
ServletContext getServletContext();
AsyncContext startAsync();
AsyncContext startAsync(ServletRequest var1, ServletResponse var2);
boolean isAsyncStarted();
boolean isAsyncSupported();
AsyncContext getAsyncContext();
DispatcherType getDispatcherType();
}
public interface ServletResponse {
//存放cookie
void Cookie();
String getCharacterEncoding();
String getContentType();
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentType(String var1);
void setBufferSize(int var1);
int getBufferSize();
void flushBuffer() throws IOException;
void resetBuffer();
boolean isCommitted();
void reset();
void setLocale(Locale var1);
Locale getLocale();
}
public interface HttpServletRequest extends ServletRequest {
String BASIC_AUTH = "BASIC";
String FORM_AUTH = "FORM";
String CLIENT_CERT_AUTH = "CLIENT_CERT";
String DIGEST_AUTH = "DIGEST";
String getAuthType();
Cookie[] getCookies();
long getDateHeader(String var1);
String getHeader(String var1);
Enumeration getHeaders(String var1);
Enumeration getHeaderNames();
int getIntHeader(String var1);
String getMethod();
String getPathInfo();
String getPathTranslated();
String getContextPath();
String getQueryString();
String getRemoteUser();
boolean isUserInRole(String var1);
Principal getUserPrincipal();
String getRequestedSessionId();
String getRequestURI();
StringBuffer getRequestURL();
String getServletPath();
HttpSession getSession(boolean var1);
HttpSession getSession();
boolean isRequestedSessionIdValid();
boolean isRequestedSessionIdFromCookie();
boolean isRequestedSessionIdFromURL();
/** @deprecated */
boolean isRequestedSessionIdFromUrl();
boolean authenticate(HttpServletResponse var1) throws IOException, ServletException;
void login(String var1, String var2) throws ServletException;
void logout() throws ServletException;
Collection getParts() throws IOException, IllegalStateException, ServletException;
Part getPart(String var1) throws IOException, IllegalStateException, ServletException;
}
public interface HttpServletResponse extends ServletResponse {
int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;
int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
int SC_EXPECTATION_FAILED = 417;
int SC_INTERNAL_SERVER_ERROR = 500;
int SC_NOT_IMPLEMENTED = 501;
int SC_BAD_GATEWAY = 502;
int SC_SERVICE_UNAVAILABLE = 503;
int SC_GATEWAY_TIMEOUT = 504;
int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
void addCookie(Cookie var1);
boolean containsHeader(String var1);
String encodeURL(String var1);
String encodeRedirectURL(String var1);
/** @deprecated */
String encodeUrl(String var1);
/** @deprecated */
String encodeRedirectUrl(String var1);
void sendError(int var1, String var2) throws IOException;
void sendError(int var1) throws IOException;
void sendRedirect(String var1) throws IOException;
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
void setStatus(int var1);
/** @deprecated */
void setStatus(int var1, String var2);
int getStatus();
String getHeader(String var1);
Collection getHeaders(String var1);
Collection getHeaderNames();
}
我在上面贴了部分源码试图找找getAttribute、addCookie、转发、状态码等方法在哪里,就加深了对这些请求响应的理解
简单来说就是一下几句:
1、WebClient向Servlet容器(Tomcat)发出Http请求;
2、Servlet容器接受Web Client的请求;
3、Servlet容器创建一个HttpRequest对象,将Web Client请求的信息封装到这个对象中;
4、Servlet容器创建一个HttpResponse对象;
5、Servlet容器调用HttpServlet对象的service()方法,把HttpRequest对象与HttpResponse对象作为参数传给HttpServlet对象;
6、HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息
7、HttpServlet调用HttpResponse对象的有关方法,生成响应数据;
8、Servlet容器把HttpServlet的响应结果传给Web Client