这几天上班时间(
)没什么别的大事,就自己看了一下servlet的api,已经把源码放进项目里,边看api边看源码,自己写一点心得体会,也是对这个的一些小总结,希望让自己有点提升。废话不多说开始写。
servlet属于服务器端的程序,也就是说它的职责是充当客户请求和服务器响应的中间层。servlet的生命周期一般是首先装载servlet,也就是启动服务的时候,服务器端调用servlet的init()方法进行初始。当一个客户端请求到达服务器,服务器首先先生成一个请求对象(
ServletRequest)和一个响应对象(ServletResponse),然后服务激活servlet的service()方法,将刚刚的请求对象和响应对象进行传递,通过请求对象,对对象的信息,请求的资源进行封装,再通过
服务端将响应对象传递回客户端。一般servlet只需要创建一次就可以(也就是只调用一次init()方法),当不再需要servlet的时候就调用destory()方法销毁servlet对象。servlet里的
接口Servlet正是各种Servlet的接口,也就是说,所有的Servlet都必须实现这个接口。
public abstract interface Servlet {
public abstract void init(ServletConfig paramServletConfig)
throws javax.servlet.ServletException;
public abstract ServletConfig getServletConfig();
public abstract void service(ServletRequest paramServletRequest,
ServletResponse paramServletResponse)
throws javax.servlet.ServletException, IOException;
public abstract String getServletInfo();
public abstract void destroy();
}
比如GenericServlet,就是实现了Servlet接口,并且GenericServlet是属于无
协议的,通用的Servlet,这里我们来看下GenericServlet的代码:
public abstract class GenericServlet implements Servlet, ServletConfig,
Serializable {
private transient ServletConfig config;
public String getInitParameter(String name) {
return getServletConfig().getInitParameter(name);
}
public Enumeration<String> getInitParameterNames() {
return getServletConfig().getInitParameterNames();
}
public ServletConfig getServletConfig() {
return this.config;
}
public ServletContext getServletContext() {
return getServletConfig().getServletContext();
}
public void init(ServletConfig config)
throws javax.servlet.ServletException {
this.config = config;
init();
}
public abstract void service(ServletRequest paramServletRequest,
ServletResponse paramServletResponse)
throws javax.servlet.ServletException, IOException;
public String getServletName() {
return this.config.getServletName();
}
}
当然,这里的GenericServlet 并不是完整的,因为一些无用的或者是抽象的方法,我就没有放进去,具体想要了解全部的可以自行下载其源码进行查看。GenericServlet之所以是无协议的,是因为service()方法依旧是需要继承的类进行实现的,整个类里有ServletConfig对象,该对象所实现的接口就是ServletConfig接口,代码为:
public abstract interface ServletConfig
{
public abstract String getServletName();
public abstract ServletContext getServletContext();
public abstract String getInitParameter(String paramString);
public abstract Enumeration<String> getInitParameterNames();
}
也就是说,servletConfig对象里存储了servlet的一些配置信息,包括ServletContext等,这样就方便实现servlet之间消息的传递。而实现servlet容器之间消息传递的方法的,就是其中的ServletContext,部分源码如下:
public abstract interface ServletContext {
public abstract ServletContext getContext(String paramString);
public abstract String getContextPath();
public abstract String getMimeType(String paramString);
public abstract Set<String> getResourcePaths(String paramString);
public abstract URL getResource(String paramString)
throws MalformedURLException;
public abstract InputStream getResourceAsStream(String paramString);
public abstract RequestDispatcher getRequestDispatcher(String paramString);
public abstract RequestDispatcher getNamedDispatcher(String paramString);
public abstract void log(String paramString);
public abstract void addListener(String paramString);
public abstract <T extends EventListener> T createListener(
Class<T> paramClass) throws javax.servlet.ServletException;
public abstract ClassLoader getClassLoader();
public abstract JspConfigDescriptor getJspConfigDescriptor();
}
这些代码里有获得文件MIME类型的,还有分发请求的,写日志的等等。每个虚拟机都会为web项目分配context(web项目是一批servlet集合, 它的内容安装在服务器上特定URL命名空间的自己下,例如/catalog,抑或通过 .war文件安装在服务器上)。这里有一个方法getRequestDispatcher()返回了RequestDispatcher,我们来看下RequestDispatcher接口的内容
public abstract interface RequestDispatcher
{
/**省略很多字符常量 **/
/**
* 将Servlet中的一个请求转交给服务器上的另一个资源(例如 sevlet、JSP文件或HTML文件), 方法允许Servlet在生成响应之前对请求和被转发到的那个资源进行预处理.
* @param paramServletRequest
* @param paramServletResponse
* @throws javax.servlet.ServletException
* @throws IOException
*/
public abstract void forward(ServletRequest paramServletRequest, ServletResponse paramServletResponse)
throws javax.servlet.ServletException, IOException;
/**
* 将资源(例如 servlet、JSP页面或HTML文件)内容包含到响应流中. 实质上方法默认启用了服务端按队列包含文件.
* @param paramServletRequest
* @param paramServletResponse
* @throws javax.servlet.ServletException
* @throws IOException
*/
public abstract void include(ServletRequest paramServletRequest, ServletResponse paramServletResponse)
throws javax.servlet.ServletException, IOException;
}
其实一看就很清楚,这个RequestDispatcher就是定义一个可以接受客户端请求并且能转发到服务器上的任何一种资源的对象,.Servlet容器创建的那个RequestDispatcher对象已经事先封装好了服务器端本地(或通过参数名提供给服务器的)资源。也就是先通过forward()方法分发请求,再通过include()方法将返回的资源包含进响应(response)里。
下面我们来看下HttpServlet这个类,这是一个通过扩展子类创建兼容Web站点的
HTTP Servlet的抽象类. HttpServlet子类必须至少重写接口中的一个方法(doPost,doGet,doPut,doDelete)。
public abstract class HttpServlet extends GenericServlet {
/**
* 继承HttpServlet的所有子类,必须重写一个方法,通常是以下四个之一
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
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 doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {}
protected void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {}
protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {}
}
再然后是两个
重载的service方法,但是一般不需要重写,但是我个人觉得这个service()方法在整个HttpServlet中扮演了重要的角色。
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 {
long lastModified;
String method = req.getMethod();
if (method.equals("GET")) {
lastModified = getLastModified(req);
if (lastModified == -1L) {
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader("If-Modified-Since");
if (ifModifiedSince < lastModified / 1000L * 1000L) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals("POST")) {
doPost(req, resp);
} else if (method.equals("PUT")) {
doPut(req, resp);
} else if (method.equals("DELETE")) {
doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
doOptions(req, resp);
} else if (method.equals("TRACE")) {
doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
第一个service()方法很好
理解,是将客户端的请求,组装成http的形式,然后交给第二个service()方法,而第2个service()方法,我们大致可以理解下整个代码的流程,就是判断是否是http的请求,然后根据判断请求的方法,分发给不同的doXXX方法,然后进行处理,也就是我们之前提到的 doPost,doGet,doPut,doDelete等方法。然后的doXXX方法,其实就是我们常说的mvc中Model层所做的业务代码。
再来看一下service()方法中传递的参数,ServletRequest和ServletResponse。首先来看ServletRequest:
public abstract interface ServletRequest{
public abstract Object getAttribute(String paramString);
public abstract Enumeration<String> getAttributeNames();
public abstract String getCharacterEncoding();
public abstract void setCharacterEncoding(String paramString)
throws UnsupportedEncodingException;
public abstract int getContentLength();
public abstract String getContentType();
public abstract ServletInputStream getInputStream()
throws IOException;
public abstract String getProtocol();
public abstract String getScheme();
public abstract String getServerName();
public abstract int getServerPort();
public abstract BufferedReader getReader()
throws IOException;
public abstract String getRemoteAddr();
public abstract String getRemoteHost();
.......
很显然,ServletRequest只不过是各种请求的一个接口,也就是说,所有的请求都会有这些方法,那么我们来看下这些方法名的意思,都是获得请求的各种参数,比如
编码,协议,服务名,端口,请求IP和一些输入流对象(写请求的信息)等等。再看看ServletResponse:
public abstract interface ServletResponse
{
public abstract String getCharacterEncoding();
public abstract String getContentType();
public abstract ServletOutputStream getOutputStream()
throws IOException;
public abstract PrintWriter getWriter()
throws IOException;
public abstract void setCharacterEncoding(String paramString);
public abstract void setContentLength(int paramInt);
public abstract void setContentType(String paramString);
public abstract void setBufferSize(int paramInt);
public abstract int getBufferSize();
public abstract void flushBuffer()
throws IOException;
public abstract void resetBuffer();
public abstract boolean isCommitted();
public abstract void reset();
public abstract void setLocale(Locale paramLocale);
public abstract Locale getLocale();
}
ServletResponse里所包含的,也就是一些响应的信息,包括是否重置,还有输出流的一些对象(和相应的数据),因为一般的响应都包含资源或者是html页面信息还有编码等。
但是,ServletRequest和ServletResponse如果直接暴露在代码中,也就是会被服务器和客户端看到,这样是不安全,所以,就出现了相应的Wapper类,也就是我们常说的包装器,将ServletRequest和ServletResponse放进ServletRequestWrapper和ServletResponseWrapper就安全了,既获得了信息,也保护了信息的安全。这里就不直接列举源码了,因为Wapper里所有的方法返回的全部都是request和response的相应方法的结果。
这里,源码包里还包含很多的Listener和Event,这些都是java里的事件机制,就是
监听器一旦
发现有请求过来,然后就开始做的事情。因为关于监听事件的类型,又有一大堆的东西可以说,这里主要还是自己写下Servlet,有时间可以写下监听事件的东西(不过事先需要让我再去补习下概念啊,哈哈哈哈)。而包里所有的ServletContextListener和ServletRequestListener都是监听器,都包含了相应内容(这个该不用解释是什么内容吧)初始方法和销毁方法。ServletContextAttributeListener都是上下文中的属性发生变化时的监听器,有add,remove和replace三个方法。ServletContextEvent,ServletContextAttributeEvent等几个事件对象的接口也都包装了获得context(上下文)或request来进行事件的信息传递。
还有就是Filter,顾名思义就是过滤器,我们很多的项目中都会用到过滤器,而
最多的就是编码过滤器和请求过滤器,都是通过实现这个接口来完成的,接口内容如下:
public abstract interface Filter {
public abstract void init(FilterConfig paramFilterConfig)
throws javax.servlet.ServletException;
public abstract void doFilter(ServletRequest paramServletRequest,
ServletResponse paramServletResponse, FilterChain paramFilterChain)
throws IOException, javax.servlet.ServletException;
public abstract void destroy();
}
大多数情况下,我们只要将doFilter()方法重写,将其配置到web.xml里,那么客户端每次的请求都会经过所配置的过滤器。
最后还有两个类,一个是cookie,还有一个是HttpSession,都是在javax.servlet.http包里的,也就是我们常用的,用来储存信息的东西。cookie存储了servlet发送给web浏览器的端少量信息,浏览器接收保存了Cookie信息, 以便稍后将这些信息发送给服务器.cookie值可以唯一标识一个客户端,因而通常cookie用于会话管理。而session提供了一种确定的用户跨越多个页面请求或访问网站和存储用户的相关信息。具体的区别和内容,这里就不多做解释了,相信只要有一点电经验的人对这两个对象不会陌生。
文章就写到这里,也是我第一次这么详细的写自己看的东西。希望各位在阅读的同时能给予指正我写错了或者是理解错了的地方,或者有能提供更好的文章让我有质的理解的,都望勿吝字,谢谢!