一、servlet的生命周期
我们通过一个完整的访问来分析servlet的生命周期:
1、访问http://localhost:8080
浏览器解析主机名,现在本地机器的host中查找对应的ip,如果没有找到,则会在DNS中查找相应的ip
2、与服务器的第一次链接
如果在DNS中找到相应的IP,客户端会和服务器尝试建立第一次链接,如果可以链接到服务器,链接建立,客户端会想服务器发送HTTP请求
3、服务器解析HTTP请求并响应
当服务器接受到HTTP请求之后,会分析HTTP请求文件,从而得知客户端想访问哪个文件,让后服务器通过查询web.xml配置文件,找到相应的文件,并响应客户端。
此时,所找到的文件也就是servlet文件,客户端与servlet发生交互,servlet的生命周期开始。
servlet的具体工作是通过反射机制创建实例,并将实例装载到内存。
WEB服务器(一般就指的是TOMCAT)把HTTP请求封装成httprequest对象,作为参数传递给service方法,每一次的servlet访问都会调用一次service方法(但是servlet只会初始化一次),servlet将结果封装在httpresponse对象中,并返回给服务器。
servlet只会初始化一次,然后每次访问都调用的是同一个servlet,所以说servlet是单例的,是线程不安全的。
一般只有三种情况会调用servlet的destroy方法来销毁servlet:
a.当站点被reload
b.关闭TOMCAT
c.服务器关机
servlet的生命周期从servlet第一次被初始化后,可以被所有用户使用,再到最后被销毁,从内存中释放出来。
二、开发servlet的三种方法
1、实现servlet方法(最基本的方法)
在使用servlet开发的时候,必须了解servlet的部署。servlet的部署是通过配置web.xml文件来实现的,web.xml的代码如下:
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0" metadata-complete="true">
<display-name>Welcome to Tomcat</display-name>
<description>Welcome to Tomcat</description>
<!--根据servlet规范,需要将servlet部署到web.xml-->
<servlet>
<!--配置资源名,习惯上一样-->
<servlet-name>Helloworld</servlet-name>
<!--指明该servlet放在那个包下面,也就是servlet的绝对路径-->
<servlet-class>com.sevend.servlet.Helloworld</servlet-class>
</servlet>
<!--servlet的映射-->
<servlet-mapping>
<!--这里的名字必须和servlet-name一样-->
<servlet-name>Helloworld</servlet-name>
<!--访问该servlet资源的路径-->
<!--该路径不一定是servlet的绝对存储的路径,可以是程序员自定义-->
<!--默认命名规范就是该servlet的名字-->
<url-pattern>/helloworld</url-pattern>
</servlet-mapping>
</web-app>
一般,一个完整的web.xml的配置文件就是这样的,至于web.xml的标签含义,以及URL的匹配关系,有专门的笔记。
使用servlet接口开发的简单示例:
package com.sevend.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class Helloworld implements Servlet
{
//初始化servlet,将servlet加载到内存中,该函数只会被调用一次
public void init(ServletConfig config)
throws ServletException
{
}
//得到一个servletconfig对象
public ServletConfig getServletConfig()
{
return null;
}
//服务函数,业务逻辑代码都写在这里
//每次请求都会被调用
public void service(ServletRequest req,ServletResponse res)
throws ServletException,java.io.IOException
{
//在控制台输出,并不会在浏览器输出
System.out.println("Hello,world!"+new java.util.Date());
//利用response对象的方法来在浏览器端输出
res.getWriter().println("hello,world!"+new java.util.Date().toLocaleString());
}
//得到servlet的一些配置信息
public java.lang.String getServletInfo()
{
return null;
}
//销毁该servlet,从内存中清除servlet,该函数只会被调用一次
public void destroy()
{
}
}
在上面的例子中实现了servlet的接口的5个方法,5个方法的作用及含义
三、通过继承GenericServlet来开发servlet
package com.sevend.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class MyGenericServlet extends GenericServlet
{
public void service(ServletResquest req,ServletResponse res)
{
res.getWriter().println("Hello,world!");
}
}
通过继承GenericServlet类来开发Servlet,将原有的servlet接口的5个函数简化成了一个函数,也可以说是只保留了service函数,这种开发较之通过实现servlet接口的方法来开发是简单了许多,但是由于后来又有了新的技术,所以该方法使用的不是很广泛。在学习过程中只需了解就成。
四、通过继承HttpServlet方法来开发servlet——这是目前在servlet开发中使用最广泛的一种方法
package com.sevend.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class MyHttpServlet extends HttpServlet
{
protected void doGet(HttpServletRequest req,HttpServletResponse resp)
throws ServletException,java.io.IOException
{
resp.getWriter().println("HttpServlet doGet() method");
}
protected void doPost(HttpServletRequest req,HttpServletResponse resp)
throws ServletException,java.io.IOException
{
resp.getWriter().println("HttpServlet doPost() method");
}
}
通过继承HttpServlet类来开发servlet,可以看出只有两个方法doGet(HttpServletRequest req,HttpServletResponse resp)和doPost(HttpServletRequest req,HttpServletResponse resp),有时候也会将这两种方法合二为一,只需通过一行代码就能够实现。this.doGet(req,resp);/this.doPost(req,resp);
在这里要稍微了解一下post和get提交方式的区别
1、get提交的内容会显示在地址栏中,但是post不会,所以post提交比get提交安全性高
2、get提交的内容不能大于2K,但是post提交内容的大小没有限制(一般不要超过64K)
3、get的响应速度要优于post
五、关于servlet单例问题
单例问题最突出的就是关于并发的处理机制,目前我们对于这种问题的解决办法可以通过同步机制来解决,就是对容易出现并发问题而无法及时修改的全局变量采用同步的机制
Synchronize(this){ code....}
在定义变量的时候也要注意单例问题,特别是定义全局变量的时候一定要慎重。