很长时间没接触servlet了,重新认识一下,下面我们需要研究一下几个问题,由于内容较长,几个问题将分为几篇文章详解
1、什么是servle
2、servlet的生命周期
3、servle的继承结构
4、servlet中的几个对象详解
5、HttpServlResponse和HttpServlRequest详解
什么是servlet
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,主要功能在于交互式地浏览和修改数据,生成动态Web内容。
狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。 —–百度百科
Servlet的程序用来处理请求和发送响应,并且Servlet是为了解决实现动态页面而衍生的东西。 浏览器通过网址来访问服务器,服务器接受请求交给servlet进行处理,然后进行响应。
tomcat和Servlet的关系
Tomcat 是Web应用服务器,是一个Servlet/JSP容器. Tomcat 作为Servlet容器,负责处理客户请求,把请求传送给Servlet,并将Servlet的响应传送回给客户.而Servlet是一种运行在支持Java语言的服务器上的组件. Servlet最常见的用途是扩展Java Web服务器功能,提供非常安全的,可移植的,易于使用的CGI替代品.
从http协议中的请求和响应可以得知,浏览器发出的请求是一个请求文本,而浏览器接收到的也应该是一个响应文本。
①:Tomcat将http请求文本接收并解析,然后封装成ServletRequest(在HttpServlet类中强转为HttpServletRequest)类型的request对象,所有的HTTP头数据读可以通过request对象调用对应的方法查询到。
②:Tomcat同时会要响应的信息封装为ServletResponse(在HttpServlet类中强转为HttpServletRresponse)类型的response对象,通过设置response属性就可以控制要输出到浏览器的内容,然后将response交给tomcat,tomcat就会将其变成响应文本的格式发送给浏览器
Java Servlet API 是Servlet容器(tomcat)和servlet之间的接口,它定义了serlvet的各种方法,还定义了Servlet容器传送给Servlet的对象类,其中最重要的就是ServletRequest和ServletResponse。所以说我们在编写servlet时,需要实现Servlet接口,按照其规范进行操作。
servlet的生命周期
servlet生命周期如下:
Servlet 加载—>实例化—>服务—>销毁。
装载和实例化为一个整体,同时进行
init():
在Servlet的生命周期中,仅执行一次init()方法。它是在服务器装入Servlet时执行的,负责初始化Servlet对象。可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init()。
service():
它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
destroy():
仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。
创建Servlet对象的时机:
Servlet容器启动时:读取web.xml配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,同时将ServletConfig对象作为参数来调用Servlet对象的init方法。 在Servlet容器启动后:客户首次向Servlet发出请求,Servlet容器会判断内存中是否存在指定的Servlet对象,如果没有则创建它,然后根据客户的请求创建HttpRequest、HttpResponse对象,从而调用Servlet对象的service方法。
Servlet容器在启动时自动创建Servlet,这是由在web.xml文件中为Servlet设置的属性决定的。从中我们也能看到同一个类型的Servlet对象在Servlet容器中以单例的形式存在。
这种方式必须在web.xml中描述:
<servlet>
...
<load-on-startup>
number
</load-on-startup>
</servlet>
number<0: 采用延迟装载,只有在第一次请求的容器才在该servlet调用初始化函数
number>=0: 采用预先装载,容器在应用启动时就加载并初始化这个servlet
number越小越先被装载,number越大越晚被装载
servlet的继承结构
servlet的继承结构如下:
1、为什么会出现ServletResponse、ServletRequest与HttpServletResponse、HttpServletRequest
从继承机构可以看到HttpServletResponse、HttpServletRequest是从HttpServlet中开始出现的,查看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);
}
在HttpServlet中将ServletResponse、ServletRequest强转为HttpServletResponse、HttpServletRequest,为什么如此做?
ServletResponse、ServletRequest、HttpServletRequest、HttpServletResponse都只是接口
public interface HttpServletRequest extends ServletRequest
而request对象实际上的类型是org.apache.catalina.connector.RequestFacade
Tomcat中的源码
public class RequestFacade implements HttpServletRequest
2、servlet的生命周期中,可以看出,执行的是service方法,为什么我们就只需要写doGet和doPost方法呢?
service()是在javax.servlet.Servlet接口中定义的, 在 javax.servlet.GenericServlet中实现了这个方法, 而 doGet/doPost 则是在 javax.servlet.http.HttpServlet 中实现的。
HttpServlet 里的三个方法:
- service(HttpServletRequest req, HttpServletResponse resp)
- doGet(HttpServletRequest req, HttpServletResponse resp)
- doPost(HttpServletRequest req, HttpServletResponse res)的区别和联系:
在servlet中默认情况下,无论你是get还是post 提交过来 都会经过service()方法来处理,然后转向到doGet 或是doPost方法,我们可以查看HttpServlet 类的service方法: 我在tomcat的lib目录下,解压servlet-api.jar,然后用反编译软件把lib\javax\servlet\http下的HttpServlet.class反编译,看里头的service()方法的原代码:
注意,sun只是定义了servlet接口,而实现servlet接口的就是类似于tomcat的服务器,所以我是在tomcat的安装目录下找到实现的类。
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if(method.equals("GET"))
{
long 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"))
{
long 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是用来转向的,但是如果你在自己的servlet类中覆盖了service方法,比如说你的service是这样的:
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
res.getOutputStream().print(
"this is my service");
}
那么这时service就不是用来转向的,而是用来处理业务的,现在不论你的客户端是用pos还是get来请求此servlet
都会执行service方法也只能执行servlet方法,不会去执行doPost或是doGet方法。
3、为什么GenericServlet有两个init方法?
我们可以看到GenericServlet抽象类中
private transient ServletConfig config;
public ServletConfig getServletConfig() {
return config;
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
init(ServletConfig config)方法,因为只有init(ServletConfig config)中带有ServletConfig对象,为了方便能够在其他地方也能直接使用ServletConfig对象,而不仅仅局限在init(ServletConfig config)方法中,所以创建一个私有的成员变量config,在init(ServletConfig config)方法中就将其赋值给config,然后通过getServletConfig()方法就能够获取ServletConfig对象了
还有一个init()方法,并且这个init()方法是空的,什么都没有,这是为什么呢?这个原因是为了防止一件事情,当我们需要在init方法中做一点别的事情,我们想到的方法就是继承GenericServlet并且重写了init(ServletConfig config)方法,这样依赖,就破坏了原本在GenericServlet类中init(ServletConfig config)写的代码了,也就是在GenericServlet类中的成员变量config会一直是null,无法得到赋值,因为被重写了,就不会在执行GenericServlet中init(ServletConfig config)方法中的代码。要想赋值,就必须在重写的init(ServletConfig config)方法中调用父类的init(ServletConfig config)方法,也就是super.init(ServletConfig config),这样就很麻烦,每次都要手动赋值一遍,所以在GenericServlet类中增加一个init()方法,以后需要在init方法中做什么,只需要重写init()这个方法,而不需要去覆盖init(ServletConfig config)这个方法,这样设计很合理,不用在管init(ServletConfig config)这个方法。
闲言碎语
本篇文章详细介绍了什么是servlet和servlet的生命周期和继承机构,对servlet有了一个详细的了解,但servlet中的几个对象我们还没有详细解释,下篇文章我们将详细研究servlet中的几个特殊对象和servlet的部分使用。