servlet 入门详解

Servlet是一种服务器端的编程语言,是J2EE中比较关键的组成部分。Servlet本质上也是Java类,编写Servlet需要遵循java的基本语法,但是与一般的Java类不同的是Servlet是只能运行在服务器端的Java类,而且必须遵循特殊的规范,在运行过程中有自己的生命周期,这些特性都是Servlet所独有的。另外Servlet和HTTP协议是紧密联系的,所以使用Servlet几乎可以处理HTTP协议所有方面的内容。

Servlet容器

一般用Tomcat来做Servlet容器,Tomcat的容器分为四个等级,真正管理Servlet的容器是Context容器,一个Context对应一个Web工程,Context容器是直接管理Servlet在容器中的包装类Wrapper,所以Context容器如何运行将直接影响Servlet的工作方式。

Servlet容器

工作流程

Servlet工作流程

当客户端浏览器向服务器请求一个 Servlet 时,服务器收到该请求后,首先到容器中检索与请求匹配的 Servlet 实例是否已经存在。

a. 若不存在,则 Servlet 容器负责加载并实例化出该类 Servlet的一个实例对象,接着容器框架负责调用该实例的 init() 方法来对实例做一些初始化工作,然后Servlet 容器运行该实例的 service() 方法。

b. 若 Servlet 实例已经存在,则容器框架直接调用该实例的 service() 方法。service() 方法在运行时,自动派遣运行与用户请求相对应的 doXX() 方法来响应用户发起的请求。通常每个Servlet类在容器中只存在一个实例,每当请求到来时,则分配一条线程来处理该请求。

处理请求流程如下:

1、Servlet容器会创建一个请求对象ServletRequst,其中封装了用户请求的信息,以便处理客户端请求,此外还会创建一个响应对象ServletResponse,用于响应客户端请求,想客户端返回数据。

2、然后Servlet容器把创建好的ServletRequst和ServletResponse对象传给用户所请求的Servlet。

3、Servlet利用ServletResponse包含的数据和自身的业务逻辑处理请求,并把处理好的结果写在ServletResponse中,最后Servlet容器把响应结果传给用户。

生命周期

1. 加载和实例化

如果Servlet容器还没实例化一个Servlet对象,此时容器装载和实例化一个 Servlet。创建出该 Servlet 类的一个实例。如果已经存在一个Servlet对象,此时不再创建新实例。

2. 初始化

在产生 Servlet 实例后,容器负责调用该 Servlet 实例的 init() 方法,在处理用户请求之前,来做一些额外的初始化工作。

3. 处理请求

当 Servlet 容器接收到一个 Servlet 请求时,便运行与之对应的 Servlet 实例的 service() 方法,service() 方法根据用户的请求调用相对应的doGet或doPost 方法来处理用户请求。然后再进入对应的方法中调用逻辑层的方法,实现对客户的响应。

4. 销毁

当 Servlet 容器决定将一个 Servlet 从服务器中移除时 ( 如 Servlet 文件被更新 ),便调用该 Servlet 实例的 destroy() 方法,在销毁该 Servlet 实例之前,来做一些其他的工作。

注意:(1)(2)(4) 在Servlet的整个生命周期中只会被执行一次。

创建Servlet对象

1. Servlet容器启动时:读取web.xml配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,同时将ServletConfig对象作为参数来调用Servlet对象的init方法。

2. 在Servlet容器启动后:客户首次向Servlet发出请求,Servlet容器会判断内存中是否存在指定的Servlet对象,如果没有则创建它,然后根据客户的请求创建HttpRequest、HttpResponse对象,从而调用Servlet 对象的service方法。

3. Servlet容器在启动时自动创建Servlet,这是由在web.xml文件中为Servlet设置的<load-on-startup>属性决定的。从中我们也能看到同一个类型的Servlet对象在Servlet容器中以单例的形式存在。web.xml配置如下:

<servlet>
        <servlet-name>Init</servlet-name>
        <servlet-class>org.xl.servlet.InitServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
</servlet>

Servlet线程安全

当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用service方法,因此,service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。大家重点理解下面两句即可:

(1) Servlet是单实例多线程的,如果存在可以修改的成员变量将会出现线程安全问题。

(2) 使用Servlet最好保证Servlet是无状态的,也就是没有可以修改的成员变量。

Servlet部署

客户端通过URL地址访问web服务器中的资源,所以若想访问Servlet必须要把servlet程序映射到一个URL地址上,在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成,如上面的实例。

1. <servlet>用于注册Servlet,包含了<servlet-name>和<servlet-class>两个子元素,分别用于设置servlet的名称以及servlet的类名。

2. <servlet-mapping>用于映射上面<servlet>中的对外访问路径,同样包含<servlet-name>和<url-pattern>两个元素,分别用于设置servlet的名称以及servlet的对外访问路径。

在servlet映射到URL有两种格式:

1.“*.扩展名”

2.以正斜杠(/)开头并以“/*”结尾。

完整请求流程

servlet工作流程图

1. Web Client 向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。

例子

//test.html
<html>  
    <head>  
    <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />  
        <title>登录</title>  
    </head>  
    <body>  
          
        <form action="http://localhost:8080/loginServlet/LoginServlet" method="post">  
            用户:<input type="text" name="username" /><br/>  
            密码:<input type="password" name="password" /><br/>  
            <input type="submit" value="登录" />  
              
        </form>  
    </body>  
</html>
//LoginServlet.jsp
import java.io.*;  
import javax.servlet.*;  
import javax.servlet.http.*;  
  
public class LoginServlet extends HttpServlet {  
    //重写doGet方法  
    public void doGet(HttpServletRequest request,  
            HttpServletResponse response)  
     throws ServletException,  
            IOException {  
        String username = request.getParameter("username");     
        String password = request.getParameter("password");        
          
        //服务器端打印信息  
        //System.out.println("username=" + username);  
        //System.out.println("password=" + password);  
        //设置编码格式  
        response.setContentType("text/html;charset=GB18030");  
          
        //返回html页面  
        response.getWriter().println("<html>");  
        response.getWriter().println("<head>");     
        response.getWriter().println("<title>登录信息</title>");      
        response.getWriter().println("</head>");    
        response.getWriter().println("<body>");     
        response.getWriter().println("欢迎【" + username + "】用户登录成功!!!");    
        response.getWriter().println("</body>");    
        response.getWriter().println("</html>");  
        }                     
    //重写doPost方法  
    public void doPost(HttpServletRequest request,  
            HttpServletResponse response)  
    throws ServletException,  
            IOException {  
        doGet(request, response);                     
    }       
}  
//web.xml
<?xml version="1.0" encoding="UTF-8"?>  
<web-app version="2.4"   
    xmlns="http://java.sun.com/xml/ns/j2ee"   
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee   
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">  
    <servlet>  
        <servlet-name>MyServlet</servlet-name>  
        <servlet-class>LoginServlet</servlet-class>  
    </servlet>  
  
    <servlet-mapping>  
        <servlet-name>MyServlet</servlet-name>  
            <url-pattern>/LoginServlet</url-pattern>  
    </servlet-mapping>  
</web-app>

执行结果:

servlet例子 servlet例子

总结

1. 当servlet被部署在应用服务器中(应用服务器中用于管理Java组件的部分被抽象成为容器)以后,由容器控制servlet的生命周期。

2. 除非特殊制定,否则在容器启动的时候,servlet是不会被加载的,servlet只会在第一次请求的时候被加载和实例化。

3. servlet一旦被加载,一般不会从容器中删除,直至应用服务器关闭或重新启动。但当容器做内存回收动作时,servlet有可能被删除。也正是因为这个原因,第一次访问servlet所用的时间要大大多于以后访问所用的时间。

版权声明:本文为JAVASCHOOL原创文章,未经本站允许不得转载。