Servlet——概述

Servlet——概述


1.Servlet的定义:

​ Servlet 是Java Server Applet的简称,称为小服务器程序,用Java编写的服务器端程序,主要功能为:交互式地浏览和修改数据,生成动态Web内容。

​ Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

​ Servlet编程需要使用到javax.servlet 和 javax.servlet.http两个包下面的类和接口,在所有的类和接口中,javax.servlet.Servlet 接口最为重要。所有的servlet程序都必须实现该接口或者继承实现了该接口的类。
——javax.servlet.ServletConfig;
​ ——javax.servlet.ServletException;
​ ——javax.servlet.http.HttpServlet;
​ ——javax.servlet.http.HttpServletRequest;
​ ——javax.servlet.http.HttpServletResponse;
​ ——javax.servlet.http.HttpSession;
​ ——javax.servlet.http.Cookie;

2.Servlet的基本工作模式

<1>客户端请求该 Servlet;

<2>加载 Servlet 类到内存;

<3>实例化并调用init()方法初始化该 Servlet;

<4>service()(根据请求方法不同调用doGet() 或者 doPost(),此外还有doHead()、doPut()、doTrace()、doDelete()、doOptions());

<5>destroy();

<6>加载和实例化 Servlet。这项操作一般是动态执行的。然而,Server 通常会提供一个管理的选项,用于在 Server 启动时强制装载和初始化特定的 Servlet;

<7>Server 创建一个 Servlet的实例;

<8>第一个客户端的请求到达 Server;

<9>Server 调用 Servlet 的 init() 方法(可配置为 Server 创建 Servlet 实例时调用,在 web.xml 中 标签下配置 标签,配置的值为整型,值越小 Servlet 的启动优先级越高);

<10>一个客户端的请求到达 Server;

<11>Server 创建一个请求对象,处理客户端请求;

<12>Server 创建一个响应对象,响应客户端请求;

<13>Server 激活 Servlet 的 service() 方法,传递请求和响应对象作为参数;

<14>service() 方法获得关于请求对象的信息,处理请求,访问其他资源,获得需要的信息;

<15>service() 方法使用响应对象的方法,将响应传回Server,最终到达客户端。service()方法可能激活其它方法以处理请求,如 doGet() 或 doPost() 或程序员自己开发的新的方法;

<16>对于更多的客户端请求,Server 创建新的请求和响应对象,仍然激活此 Servlet 的 service() 方法,将这两个对象作为参数传递给它。如此重复以上的循环,但无需再次调用 init() 方法。一般 Servlet 只初始化一次(只有一个对象),当 Server 不再需要 Servlet 时(一般当 Server 关闭时),Server 调用 Servlet 的 destroy() 方法。

3.Servlet简单实现
3.1 Servlet核心接口和类
<1>Servlet接口:

在ServletAPI中最重要的是Servlet接口,所有Servlet都会直接或间接的与该接口发生联系,或是直接实现该接口,或间接继承自实现了该接口的类。

该接口包括以下五个方法:
init(ServletConfig config)
ServletConfig getServletConfig()
service(ServletRequest req,ServletResponse res)
String getServletInfo()
destroy( )

处理方式:
(1)第一次访问Servlet时,服务器会创建Servlet对象,并调用init方法,再调用service方法
(2)第二次再访问时,Servlet对象已经存在,不再创建,只执行service方法
(3)当服务器停止,会释放Servlet,调用destroy方法。

Servlet接口这五个方法,其中init、service、destory是生命周期方法。init和destory各自只执行一次,即servlet创建和销毁时。而service在每次有新的请求到来时都会被调用。所以我们的主要业务逻辑应该在service方法中进行实现。

servlet源码

package javax.servlet;

import java.io.IOException;

public interface Servlet {
    /**
     *  Tomcat反射创建Servlet之后(注意:创建之后),Tomcat调用init()时会传入ServletConfig对象
     */
    void init(ServletConfig var1) throws ServletException;

    ServletConfig getServletConfig();
    
	/**
     *  Tomacat将HTTP请求和响应封装成对象传给服务器
     */
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    String getServletInfo();

    void destroy();
}
<2>GenericServlet抽象类:

​ GenericServlet 使编写 servlet 变得更容易。它提供生命周期方法 init 和 destroy 的简单实现,要编写一般的 servlet,只需重写抽象 service 方法即可。

package javax.servlet;

import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    private static final long serialVersionUID = 1L;
    private transient ServletConfig config;

    public GenericServlet() {
    }
	/**
     * destroy方法并不是销毁servlet的方法,但在servlet销毁前一定会被调用
     * 主要功能是为了关闭资源
     */
    public void destroy() {
    }

    public String getInitParameter(String name) {
        return this.getServletConfig().getInitParameter(name);
    }

    public Enumeration<String> getInitParameterNames() {
        return this.getServletConfig().getInitParameterNames();
    }

    /**
     *  由于init方法时Servlet创建后最先代用的,所以getServletConfig方法调用时,
     *  成员变量config已经被赋值,此处即可获取config
     */
    public ServletConfig getServletConfig() {
        return this.config;
    }

    /**
     *  通过getServletConfig方法得到servletContext
     */
    public ServletContext getServletContext() {
        return this.getServletConfig().getServletContext();
    }

    public String getServletInfo() {
        return "";
    }
	
    /**
     *  设置一个成员变量servletConfig用于接收Tomcat传入的servletConfig对象
     *  这个设置可以提升servletConfig的作用域:局部变量-->成员变量
     */
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    public void init() throws ServletException {
    }

    public void log(String msg) {
        this.getServletContext().log(this.getServletName() + ": " + msg);
    }

    public void log(String message, Throwable t) {
        this.getServletContext().log(this.getServletName() + ": " + message, t);
    }

    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    public String getServletName() {
        return this.config.getServletName();
    }
}

GenericsServlet类进行了以下改良:

<1>提升了init方法中原本是形参的servletConfig对象的作用域(成员变量),方便其他方法进行调用

<2>init方法中还调用了一个init无参方法,如果我们希望在servlet创建时做一些初始化操作,可以继承GenericsServlet类,覆盖init空参方法

<3>HttpServlet类

HttpServlet类
是继承GenericServlet的基础上进一步的扩展。
提供将要被子类化以创建适用于 Web 站点的 HTTP servlet 的抽象类。HttpServlet 的子类至少必须重写一个方法,该方法通常是以下这些方法之一:
doGet,如果 servlet 支持 HTTP GET 请求
doPost,用于 HTTP POST 请求
doPut,用于 HTTP PUT 请求
doDelete,用于 HTTP DELETE 请求
init 和 destroy,用于管理 servlet 的生命周期内保存的资源
getServletInfo,servlet 使用它提供有关其自身的信息

//生命周期方法
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;
        try {
            /**
             *	1.强转成HTTP类型,功能更加强大
             */
            request = (HttpServletRequest)req;
            response = (HttpServletResponse)res;
        } catch (ClassCastException var6) {
            throw new ServletException(lStrings.getString("http.non_http"));
        }

        this.service(request, response);//2.调用service方法(每次都需要调用,相当于生命周期方法)
    }


 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();//3.获取请求方式
        long lastModified;
     	/**
         *	4.判断逻辑,调用不同的处理方法doPost和doGet方法
         */
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }
    

HttpServlet中的service方法已经代替我们完成了复杂的请求方法判断

public abstract class HttpServlet extends GenericServlet 
注意:HttpServlet是一个抽象类,但是在源码中可以发现,在该类中并没有抽象方法,那为什么要将HttpServlet声明为一个抽象类呢?

HttpServlet文档注释:

Provides an abstract class to be subclassed to create an HTTP servlet suitable for a Web site. A subclass of HttpServlet must override at least one method, usually one of these:
.doGet, if the servlet supports HTTP GET requests
.doPost, for HTTP POST requests
.doPut, for HTTP PUT requests
.doDelete, for HTTP DELETE requests
.init and destroy, to manage resources that are held for the life of the servlet
.getServletInfo, which the servlet uses to provide information about itself
There’s almost no reason to override the service method. service handles standard HTTP requests by dispatching them to the handler methods for each HTTP request type (the doXXX methods listed above).

文档中说明,这里提供了一个抽象类,用于被子类继承并获取与网站匹配的处理Http请求的Servlet,HttpServlet的子类至少要重写doGet、doPost、doPut、doDelete、init、destroy、getServletInfo中的一个。在最后警示:不要对service方法进行重写。

一个类声明为抽象类,一般有两个原因:有抽象方法或者没有抽象方法,但是该类不希望被实例化。

那为什么HttpServlet不希望被实例化,且要求子类重写doGet、doPost等方法呢?

我们看一下doGet等方法的源码作为对照:

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 {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_post_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(405, msg);
        } else {
            resp.sendError(400, msg);
        }
    }

doGet方法用protected进行修饰,确保子类能被修饰。

从doGet和doPost方法中可以了解到,具体的业务代码都写在方法中,Service方法只是判断并转发请求HttpServlet对七个doXxx方法做了默认实现(405报错),但这种处理十分粗暴并且无法真正的解决问题,所以,子类继承HttpServlet类后需要根据具体的业务进行重写。

3.2 Servlet的两种创建方式
<1>Servlet的第一种创建方式:继承HttpServlet(推荐使用)
/**
 * Servlet implementation class HelloServlet
 * 演示Servlet的第一种创建方式,继承HttpServlet.也是开发中推荐的
 * 
 */
@WebServlet("/hs1")
public class HelloServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
    

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		response.getWriter().print("我是Servlet创建的第一种方式");
	}
	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
}
<2>Servlet创建的第二种方式:实现接口Servlet
/**
 * Servlet创建的第二种方式:实现接口Servlet
 * */
@WebServlet("/hs2")
public class HelloServlet2 implements Servlet{

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
	}

	@Override
	public ServletConfig getServletConfig() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getServletInfo() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void init(ServletConfig arg0) throws ServletException {
		// TODO Auto-generated method stub	
	}
	@Override
	public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		System.out.println("OK");
		response.setContentType("text/html;charset=UTF-8");
		response.getWriter().println("我是第二种创建方式");
	}
}
3.3 Servlet的两种配置方式
<1>注解式配置 Servlet3.0及以后 :
/**
 * Servlet implementation class HelloServlet
 * 演示Servlet注解式配置
 */
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		response.getWriter().print("OK");
	}
	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
}

使用注解方式:

注解类 WebServlet

name:serlvet名字 (可选)

value: 配置url路径

urlPatterns:配置url路径 ,和value作用一样,不能同时使用

loadOnStartup:配置Servlet的创建的时机, 如果是0或者正数 启动程序时创建,如果是负数,则访问时创建。数字越小优先级越高。

initParams:配置Servlet的初始化参数

<2>web.xml配置 Servlet所有版本都支持:
/**
 * Servlet implementation class HelloServlet
 * 演示Servlet的web.xml配置
 */
public class HelloServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		response.getWriter().print("OK");
	}
	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
  <display-name>Web_Day11</display-name>
  <!--Servlet的第二种配置  -->
  <!--Servlet配置  -->
  <servlet>
  <!--名称  -->
    <servlet-name>hello2</servlet-name>
    <!--Servlet的全称类名  -->
    <servlet-class>com.qf.web.servlet.HelloServlet</servlet-class>
    <!--启动的优先级,数字越小越先起作用  -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <!--映射配置  -->
  <servlet-mapping>
  <!--名称  -->
    <servlet-name>hello2</servlet-name>
    <!--资源的匹配规则:精确匹配  -->
    <url-pattern>/hello2</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>login.html</welcome-file>
  </welcome-file-list>
</web-app>

容器在进行url-pattern配置的时候是遵循一定的匹配原则的
url-pattern定义匹配规则,取值说明:
精确匹配 /具体的名称 只有url路径是具体的名称的时候才会触发Servlet
后缀匹配 .xxx 只要是以xxx结尾的就匹配触发Servlet
通配符匹配 /
匹配所有请求,包含服务器的所有资源
通配符匹配 / 匹配所有请求,包含服务器的所有资源,不包括.jsp

load-on-startup :
1元素标记容器是否应该在web应用程序启动的时候就加载这个servlet。
2它的值必须是一个整数,表示servlet被加载的先后顺序。
3如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。
4如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,值越小,servlet的优先级越高,就越先被加载。值相同时,容器就会自己选择顺序来加载。

<init-param>
      <param-name>name</param-name>
      <param-value>张三</param-value>
</init-param>

[1] init-param元素用来定义Servlet启动的参数,可以定义多个
[2] param-name表示参数名称
[3] param-value表示参数值

3.4 Servlet生命周期
生命周期的四个阶段

阶段一、实例化(调用构造方法)
实例化阶段是Servlet生命周期中的第一步,由Servlet容器调用Servlet的构造器创建一个具体的Servlet对象的过程。而这个创建的时机可以是在容器收到针对这个组件的请求之后,即用了才创建;也可以在容器启动之后立刻创建实例,而不管此时Servlet是否使用的上。使用如下代码可以设置Servlet是否在服务器启动时就执行创建
1

阶段二、初始化(init方法)
Servlet在被加载实例化之后,必须要初始化它。在初始化阶段,init()方法会被调用。这个方法在javax.servlet.Servlet接口中定义。其中,方法以一个ServletConfig类型的对象作为参数。ServletConfig对象由Servlet引擎负责创建,从中可以读取到事先在web.xml文件中通过节点配置的多个name-value名值对。ServletConfig对象还可以让Servlet接受一个ServletContext对象。
一般情况下,init方法不需要编写,因GenericServlet已经提供了init方法的实现,并且提供了getServletConfig方法来获得ServletConfig对象。
注:init方法只被执行一次

阶段三、就绪/服务
Servlet被初始化以后就处于能够响应请求的就绪状态。每个对Servlet的请求由一个ServletRequest对象代表,Servlet给客户端的响应由一个ServletResponse对象代表。当客户端有一个请求时,容器就会将请求与响应对象转给Servlet,以参数的形式传给service方法。service方法由javax.servlet.Servlet定义,由具体的Servlet实现HttpServlet将service方法拆分了。doGet和doPost

阶段四、销毁
Servlet容器在销毁Servlet对象时会调用destroy方法来释放资源。通常情况下Servlet容器停止或者重新启动都会引起销毁Servlet对象的动作,但除此之外,Servlet容器也有自身管理Servlet对象的准则,整个生命周期并不需要人为进行干预

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值