个人觉得要把javaWeb那一套搞清楚,首先得要把servlet的那一套东西搞清楚吧,本来是打算要看spring的DispatcherServlet,但是发现自己这方面的基础还比较的薄弱,那么就从servlet这块补起来吧...
我们一般情况下编写servlet都会继承HttpServlet这个类型,然后重载它的doGet和doPost两个方法..
先来看看它的继承体系:
整个继承体系还是比较的简单的,那么我们就从最顶层的Servlet接口的定义开始吧:
public interface Servlet {
//初始化的时候被servlet容器调用,只有init之后,servlet才能被用于处理请求
public void init(ServletConfig config) throws ServletException;
//获取servlet的config对象,里面包括了servlet的启动参数等
public ServletConfig getServletConfig();
//当有http请求进来之后,将会调用这个方法来处理
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
//返回这个servlet的基本信息,例如版本,作者等
public String getServletInfo();
//servlet的销毁,只有所有的线程的service方法都执行了之后这个方法才能调用
public void destroy();
}
本身接口的定义比较的简单,其中有两个比较重要的方法:
(1)init方法:这个是servlet的初始化方法,在这个servlet可以用之前,servlet容器将会调用这个方法,并传入config对象,只有init之后了,这个servlet才能被用于处理http请求。
(2)service方法:这个方法就是用于处理http请求的,当一个http请求进来之后,servlet容器会找到相应的servlet然后调用service方法来处理。
接下来来看看ServletConfig这个接口的定义:
//servlet在初始化的时候,由servlet容器传递给servlet
public interface ServletConfig {
//返回servlet的名字
public String getServletName();
//返回当前servlet的执行上下文
public ServletContext getServletContext();
//获取相应的初始化参数
public String getInitParameter(String name);
//获取初始化参数名
public Enumeration getInitParameterNames();
}
前面已经提到过,servlet容器在初始化servlet的时候会传入一个config对象,它有一个方法getServletContext,用于获取当前servlet的context,每一个webApplication都有一个自己的context。
那么接下来来看看GenericServlet的定义吧:
public abstract class GenericServlet
implements Servlet, ServletConfig, java.io.Serializable
{
private transient ServletConfig config; //保证这个属性在当前对象的序列化的时候不会被保存
public GenericServlet() { }
public void destroy() {
}
//获取一个初始化参数的值
public String getInitParameter(String name) {
return getServletConfig().getInitParameter(name);
}
//返回所有初始化参数的名字,其实就相当于对congfig做了一层代理
public Enumeration getInitParameterNames() {
return getServletConfig().getInitParameterNames();
}
public ServletConfig getServletConfig() {
return config;
}
//获取当前servlet的context
public ServletContext getServletContext() {
return getServletConfig().getServletContext();
}
//当前servlet的基本信息,例如作者啥的
public String getServletInfo() {
return "";
}
//servlet容器调用这个方法来初始化servlet,servlet容器会传进来servlet的config对象
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
//以后的子类覆盖
public void init() throws ServletException {
}
//写日志
public void log(String msg) {
getServletContext().log(getServletName() + ": "+ msg);
}
public void log(String message, Throwable t) {
getServletContext().log(getServletName() + ": " + message, t);
}
//用于处理http请求
public abstract void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
//获取当前servlet的名字
public String getServletName() {
return config.getServletName();
}
}
它是一个抽象类,这里它也实现了ServletConfig接口,我们在前面已经知道,servlet在初始化的时候会由servlet容器传入这个config对象,这里实现了ServletConfig接口其实无非是对拥有的config对象做了一层代理。。。
其实它实现的方法并不多,而且都是一些基本的方法,很多方法的实现其实都是在HttpServlet中,这里我们就来看看它最重要的一个方法就可以了,service方法:
//根据不同的http请求调用不同的方法来处理
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
//如果http请求是get类型的
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); //处理get请求
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
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);
}
}
这里就可以看出,根据不同的http请求,例如get和post,调用相应的方法来处理它,因此我们自己在写servlet的时候只要重载这两个方法就可以了。。。另外因为servlet都是在线程池中执行的,所以需要考虑servlet的并发问题。。。。
那么到这里整个servlet的大体的设计就已经有了一定的了解,用户只需要编写servlet,而后其余的事情都是servlet容器来做的了。。。它会来初始化servlet,为servlet传入config对象,还有为webApplication分配context,以及当有http请求进来的时候,调用servlet的service方法来处理,同时要传入request和response对象。。
由此可见,servlet容器其实做了最多的请示。。。
这里还有一个东西补充一下,那就是ServletContext,每一个webApplication都有一个context,是由servlet容器分配的,它可以用于servlet与servlet容器之间进行交互。。。
来看看它的定义吧:
//定义了一些方法用于servlet与其容器进行交流,每一个webapplication都有一个
public interface ServletContext {
//获取一个url的context,可以用于获取别的application的context,为了安全一般情况下servlet容器会返回null
public ServletContext getContext(String uripath);
public String getContextPath();
//返回servlet的api版本
public int getMajorVersion();
//版本信息
public int getMinorVersion();
//返回一个文件的mime类型
public String getMimeType(String file);
//返回当前path下面的所有资源的路径 getResourcePaths("/catalog/") returns {"/catalog/index.html", "/catalog/products.html", "/catalog/offers/"}
public Set getResourcePaths(String path);
//返回一个资源的外面访问的url
public URL getResource(String path) throws MalformedURLException;
//返回一个资源的访问stream
public InputStream getResourceAsStream(String path);
//返回一个path的Dispatcher对象,可以用于路由http请求
public RequestDispatcher getRequestDispatcher(String path);
//通过servlet的名字,获取它的Dispatcher
public RequestDispatcher getNamedDispatcher(String name);
public Servlet getServlet(String name) throws ServletException;
public Enumeration getServlets();
public Enumeration getServletNames();
//写日志
public void log(String msg);
public void log(Exception exception, String msg);
public void log(String message, Throwable throwable);
//返回一个path的事件path,例如"/index.html" -->"http://host/contextPath/index.html
public String getRealPath(String path);
//返回servlet容器的版本信息
public String getServerInfo();
//返回一个初始化参数的值
public String getInitParameter(String name);
//返回初始化参数的名字
public Enumeration getInitParameterNames();
//获取属性
public Object getAttribute(String name);
//返回所有的属性名
public Enumeration getAttributeNames();
//设置属性
public void setAttribute(String name, Object object);
//删除属性
public void removeAttribute(String name);
//context的名字
public String getServletContextName();
}
其实这里可以看到大多数方法都是与资源的访问有关的。。。
看到这里,其实发现如果要对整个javaWeb有比较系统的认识的话,其实还需要对servlet容器的运行有一定的了解,那以后就拿jetty服务器作为入口来分析吧。。。