servlet详解

来源:互联网 发布:js split 二维数组 编辑:程序博客网 时间:2024/06/04 20:07

Servlet简介
Servlet是服务器端的重要组件,直译为服务端的小程序,它属于动态资源,用来处理请求,服务器接收到请求后会调用Servlet来处理请求。
Servlet的主要作用
接收请求
处理请求
完成响应
例如:
当我们要完成一个登录功能时,用户会将输入的用户名和密码以POST请求的形式发送到服务器,但是服务器本身并不具有能力来读取用户发送的用户名和密码,也就不可能对用户名和密码进行验证,所以当服务器收到这类请求后需要将请求转个一个Servlet处理。
Servlet实现类由我们编写,而由Web服务器(Servlet容器)调用,每个Servlet都必须实现javax.servlet.Servlet
HelloServlet
步骤:
创建动态WEB项目WEB_Servlet
项目下创建包com.atguigu.web.servlet
包下创建一个类HelloServlet并实现javax.servlet.Servlet接口
在HelloServlet的service()方法中加入一行打印语句System.out.println(“hello”);
在WEB-INF目录下的web.xml文件中注册映射Servlet

HelloServlet
com.atguigu.web.servlet.HelloServlet


HelloServlet
/HelloServlet

启动服务器,在浏览器中访问:http://localhost:8080/WEB_Servlet/HelloServlet
具体代码
类:com.atdongruan.web.servlet.HelloServlet

public class HelloServlet implements Servlet {@Overridepublic void init(ServletConfig config) throws ServletException {}@Overridepublic ServletConfig getServletConfig() {return null;}@Overridepublic void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {System.out.println("hello");}@Overridepublic String getServletInfo() {return null;}@Overridepublic void destroy() {}}

web.xml配置文件

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">    <servlet>        <servlet-name>HelloServlet</servlet-name>        <servlet-class>com.atdongruan.web.servlet.HelloServlet</servlet-class>    </servlet>    <servlet-mapping>        <servlet-name>HelloServlet</servlet-name>        <url-pattern>/HelloServlet</url-pattern>    </servlet-mapping></web-app>

web.xml文件详解:
因为创建好的Servlet需要由Servlet容器调用,而Servlet容器并不能知道我们所创建的Servlet的存在,所以需要在web.xml文件中进行注册。
servlet /servlet用于注册servlet
servlet-name用于设置servlet的名字,在服务器中关于servlet的一切配置都需要servlet-name来进行配置
servlet-class用于设置servlet的全类名,用于创建servlet的实例(反射)
而仅仅注册是远远不够的,因为Servlet是用来处理客户端发送的请求的,所以还需要为Servlet映射一个请求地址。
servlet-mapping用于映射需要servlet处理的请求地址
servlet-name为servlet的名字,和servlet中的name有对应关系
url-pattern需要servlet处理的请求地址
Servlet接口
3.1 Servlet生命周期
生命周期,以人举例的话,从人的出生到死亡称为一个人的生命周期,在人的整个生命中有很多的阶段,但总的来说可以大致分为三个阶段:出生、工作、死亡。每一阶段都有每一阶段要做的事。对于我们的Servlet也是一样。
Servlet的生命周期指的是Servlet由实例化到被销毁的过程。同样也被分为了三个阶段:实例化、处理请求、被销毁。而每个阶段我们都有对应的方法来实现响应的功能,在实例化阶段需要调用init()方法来做初始化操作,处理请求阶段调用service()方法处理请求,销毁对象之前调用destroy()做释放资源等操作。
Servlet生命周期相关方法
public void init(ServletConfig config)
public void service(ServletRequest req, ServletResponse res)
public void destroy()
3.1.1 Servlet初始化
服务器会在Servlet第一次处理请求、或服务器启动时创建Servlet实例,默认是在第一次处理请求时进行实例化的。
对于每个Servlet,服务器只会创建一个Servlet实例。以我们的HelloServlet为例,当我们通过浏览器访问http://localhost:8080/WEB_Servlet/HelloServlet时,服务器会根据路径/HelloServlet从配置文件中找到url-pattern值为/HelloServlet的servlet-mapping,然后在servlet-mapping中找到servlet-name为HelloServlet。接下来,找到servlet-name为HelloServlet的。最后,获得servlet的全类名,通过全类名创建类的实例,这个实例会放到一个集合中。这一过程后容器再也不会对该servlet做实例化操作,而是直接从集合中获取servlet实例处理请求。
Servlet实例化后会立即调用public void init(ServletConfig config)方法。这里主要做一些获取配置信息等在处理请求前需要做的准备工作。
init()方法在Servlet的整个生命周期中只会被调用一次。
init(ServletConfig config)方法被调用时,容器会传递一个ServletConfig对象作为参数,该对象可以获取Servlet相关的配置信息。

3.1.2 Servlet处理请求
当Servlet收到请求时,服务器会调用Servlet的service()方法来处理请求。每收到一次请求就调用一次service()方法,所以service()方法是被多次调用的。因此,我们开发时主要的业务都发生在service()方法中。
service(ServletRequest req, ServletResponse res)被调用时,荣器会创建一个ServletRequest和一个ServletResponse对象作为参数传递进来,ServletRequest对象主要封装了客户端发送过来的请求信息,ServletResponse对象主要用来封装了服务器响应给客户端的数据。关于这两个对象后边我们还要详细的学习,这里就先不讨论了。
Servlet销毁
一般情况下Servlet对象不会被销毁,但当服务器关闭或者重启时Servlet对象也将被销毁,这时在servlet被销毁前它的destroy()方法会被调用,该方法主要做一些销毁前的收尾工作,比如:释放资源、保存数据等。在实际应用中,这类工作主要由其他对象完成所以该方法并不常用。
Servlet相关接口
ServletRequest和ServletResponse
ServletRequest是由容器创建并传递到service()方法中,容器所创建的对象实际上是HttpServletRequest,所以开发中我们都会将ServletRequest强转成HttpServletRequest。
HttpServletRequest的方法:
String getParameter(String paramName):获取指定请求参数的值;
String getMethod():获取请求方法,例如GET或POST;
String getHeader(String name):获取指定请求头的值;
void setCharacterEncoding(String encoding):设置请求体的编码!因为GET请求没有请求体,所以这个方法只只对POST请求有效。当调用request.setCharacterEncoding(“utf-8”)之后,再通过getParameter()方法获取参数值时,那么参数值都已经通过了转码,即转换成了UTF-8编码。所以,这个方法必须在调用getParameter()方法之前调用!
HttpServletResponse的方法
PrintWriter getWriter():获取字符响应流,使用该流可以向客户端输出响应信息。例如response.getWriter().print(“ h1 Hello JavaWeb! /h1”);
ServletOutputStream getOutputStream():获取字节响应流,当需要向客户端响应字节数据时,需要使用这个流,例如要向客户端响应图片;
void setCharacterEncoding(String encoding):用来设置字符响应流的编码,例如在调用setCharacterEncoding(“utf-8”);之后,再response.getWriter()获取字符响应流对象,这时的响应流的编码为utf-8,使用response.getWriter()输出的中文都会转换成utf-8编码后发送给客户端;
void setHeader(String name, String value):向客户端添加响应头信息,例如setHeader(“Refresh”, “3;url=http://www.atguigu.com”),表示3秒后自动刷新到http:// www.atguigu.com;
void setContentType(String contentType):该方法是setHeader(“content-type”, “xxx”)的简便方法,即用来添加名为content-type响应头的方法。content-type响应头用来设置响应数据的MIME类型,例如要向客户端响应jpg的图片,那么可以setContentType(“image/jepg”),如果响应数据为文本类型,那么还要台同时设置编码,例如setContentType(“text/html;chartset=utf-8”)表示响应数据类型为文本类型中的html类型,并且该方法会调用setCharacterEncoding(“utf-8”)方法;
void sendError(int code, String errorMsg):向客户端发送状态码,以及错误消息。例如给客户端发送404:response(404, “您要查找的资源不存在!”)。

ServletConfig
ServletConfig对象对应着web.xml文件中的一个servlet元素,例如:想要获取元素中的servlet-name的值,那么可以使用servletConfig.getServletName()方法来获取。
ServletConfig对象同样有容器创建,然后作为参数传递给init()方法,可以在init()方法中使用。
ServletConfig的方法:
String getServletName():获取Servlet在web.xml文件中的配置名称,即servlet-name指定的名称;
ServletContext getServletContext():用来获取ServletContext对象,ServletContext会在后面讲解;
String getInitParameter(String name):用来获取在web.xml中配置的初始化参数,通过参数名来获取参数值;
Enumeration getInitParameterNames():用来获取在web.xml中配置的所有初始化参数名称;
在web.xml中为servlet配置初始化参数

<servlet><servlet-name>HelloServlet</servlet-name><servlet-class>省略</servlet-class><init-param><param-name>username</param-name><param-value>root</param-value></init-param></servlet>

每一个init-param表示一个初始化参数,通过getInitParameter(String name)可以根据param-name的值获取到param-value的值。
每个servlet中可以配置多个init-param,servlet只能获取自身的初始化参数,而不能获得其他servlet的初始化参数。
GenericServlet
在显示开发中我们如果直接实现servlet接口功能也是可以正常实现的,但是所面临的问题是:如果我直接实现Servlet接口,那接口中的所有方法都必须要实现,但是这些方法有的我们用不到,而有的方法实现起来很麻烦而且没给servlet实现的代码都差不多,于是我们就需要一个抽象类来帮助我们实现servlet接口,实现一些通用的方法,而我们只需要继承GenericServlet就可以了,这样的好处是我们不在需要重写全部方法,而只需要重写必须的和我们需要的方法就可以。
代码:

public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable{    private transient ServletConfig config;    public GenericServlet() { }    public void destroy() {}     public String getInitParameter(String name) {        return getServletConfig().getInitParameter(name);    }    public Enumeration getInitParameterNames() {        return getServletConfig().getInitParameterNames();    }       public ServletConfig getServletConfig()  {return config;  }    public ServletContext getServletContext() {        return getServletConfig().getServletContext();    }    public String getServletInfo() { return ""; }    public void init(ServletConfig config) throws ServletException {        this.config = config;        this.init();    }    public void init() throws ServletException {}    public void log(String msg) {        getServletContext().log(getServletName() + ": "+ msg);    }    public void log(String message, Throwable t) {        getServletContext().log(getServletName() + ": " + message, t);    }    public abstract void service(ServletRequest req, ServletResponse res)    throws ServletException, IOException;    public String getServletName() {  return config.getServletName(); }}

GenericServlet中有两个重载的init()方法,由于ServletConfig对象是在容器调用init(ServletConfig config)方法是传过来的,只能在该方法中使用所以我们在类中定义了一个类型为ServletConfig的对象,而在init(ServletConfig config)方法中对该变量进行赋值。这样做就会有一个问题存在,如果子类在继承父类时重写了该方法,那赋值操作将不会被调用,这时如果使用ServletConfig的方法将导致空指针异常。所以,在这个类中又定义了一个init()方法,这个方法将在init(ServletConfig config)方法中被调用,并且是在config对象赋值之后调用,而初始化的操作我们可以通过重写config()方法来完成,这样做既保证了对ServletConfig的赋值,又可以正常做初始化操作。
像这种将固定的代码编写到一个方法中,而将有可能发生变化的代码交给子类编写。实际上是一种叫做模板模式的设计模式,而像init()这种没有方法体,而又被父类方法调用,需要子类重写的方法称为钩子方法。
GenericServlet同时还实现了ServletConfig接口,实现这个接口的好处是我们直接通过this就可以调用ServletConfig对象的方法了,而这些方法实际调用的都是,成员变量config的方法。
HttpServlet
HttpServlet是GenericServlet的子类,他是Tomcat中专门处理HttpServlet的Servlet(实际上Tomcat只处理Http类型的请求)所以在一般情况下我们都会通过继承HttpServlet来实现servlet。
HttpServlet和GenericServlet一样都是一个抽象类,也就是不能对它们进行实例化,与GenericServlet不同HttpServlet中没有任何抽象方法,所以需要我们根据实际需求来重写它的方法。
在GenericServlet中service(ServletRequest req, ServletResponse res)方法是一个抽象方法,必须要由子类实现,而在HttpServlet中已经被实现,而在HttpServlet的实现的方法中会被强制转换成HttpServletRequest和HttpServletResponse(不用担心会出现类型转换异常,因为传过来的对象本身就是该类型)。转换类型之后会调用HttpServlet中的一个重载的service(HttpServletRequest req, HttpServletResponse resp)方法。也就是说如果我们要重写service()方法不必再去重写GenericServlet的service()方法了,而可以直接重写HttpServlet重载的service()方法就可以了。
在service(HttpServletRequest req, HttpServletResponse resp)方法中,会对它的请求类型进行判断,然后根据请求类型的不同再去调用不同的方法。如:post类型的请求回去调用doPost(),get请求回去调用doGet(),delete请求回去调用doDelete(),以此类推。但是在实际应用中我们只会使用到doPost()和doGet(),所以一般情况下我们不会去重写service()方法,而是去重写更简单的doGet()或者doPost()。
附上一个servlet工作图帮助理解。
这里写图片描述

原创粉丝点击