JavaWeb学习笔记(三)—— Servlet

一、Servlet概述

1.1 什么是Servlet

  Servlet是sun公司提供一套规范(接口),是JavaWeb的三大组件之一(Servlet、Filter、Listener),它属于动态资源。Servlet的作用是处理请求,服务器会把接收到的请求交给Servlet来处理,在Servlet中通常需要:

  • 接收请求数据;
  • 处理请求;
  • 完成响应。

  例如客户端发出登录请求,或者输出注册请求,这些请求都应该由Servlet来完成处理!Servlet需要我们自己来编写,每个Servlet必须实现javax.servlet.Servlet接口。

1.2 Servlet的实现方式

  实现Servlet有三种方式:

  • 实现javax.servlet.Servlet接口;
  • 继承javax.servlet.GenericServlet类;
  • 继承javax.servlet.http.HttpServlet类;

  Servlet类由我们来写,通常我们会去继承HttpServlet类来完成我们的Servlet,但对象由服务器来创建,并且由服务器来调用相应的方法

1.3 Servlet的运行过程

  Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
  ①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
  ②装载并创建该Servlet的一个实例对象。
  ③调用Servlet实例对象的init()方法。
  ④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
  ⑤WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。

  用户第一次访问Servlt的时候,服务器会创建一个Servlet的实例,Servlet中的init()方法就会执行。任何一次请求服务器都会创建一个新的线程访问Servlet中的service方法。在service方法内部根据请求方式的不同调用doXXX方法。(get请求调用doGet,post请求调用doPost)。当Servlet从服务器中移除掉,或者关闭服务器时,Servlet的实例就会被销毁,此时destroy方法就

二、Servlet的生命周期

  所谓xxx的生命周期,就是说xxx的出生、服务,以及死亡。Servlet生命周期也是如此!与Servlet的生命周期相关的方法有:

  • void init(ServletConfig);
  • void service(ServletRequest,ServletResponse);
  • void destroy();

2.1 Servlet的创建

  默认情况下,第一次访问servlet时服务器才会创建该对象,如果需要服务器启动时就创建Servlet,那么还需要在web.xml文件中配置:

<servlet>
    <servlet-name>hello1</servlet-name>
    <servlet-class>cn.itcast.servlet.Hello1Servlet</servlet-class>
    <load-on-startup>0</load-on-startup> 
</servlet>
<servlet-mapping>
    <servlet-name>hello1</servlet-name>
    <url-pattern>/hello1</url-pattern>
</servlet-mapping>
<servlet>
    <servlet-name>hello2</servlet-name>
    <servlet-class>cn.itcast.servlet.Hello2Servlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>hello2</servlet-name>
    <url-pattern>/hello2</url-pattern>
</servlet-mapping>
<servlet>
    <servlet-name>hello3</servlet-name>
    <servlet-class>cn.itcast.servlet.Hello3Servlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>hello3</servlet-name>
    <url-pattern>/hello3</url-pattern>
</servlet-mapping>

  在<servlet>元素中配置<load-on-startup>元素可以让服务器在启动时就创建该Servlet,其中<load-on-startup>元素的值必须是一个整数,表示servlet应该被载入的顺序,当值为0或者大于0时,表示容器在启动时就加载并初始化这个servlet;当值小于0或者没有指定时,则表示容器在该Servlet被请求时,才会去加载。正数的值越小,该Servlet的优先级就越高,应用启动时就优先加载。所以,<load-on-startuo>x</load-on-startuo>中x的取值1,2,3,4,5代表的是优先级,而非启动延迟时间。上例中,根据<load-on-startup>的值可以得知服务器创建Servlet的顺序为Hello1Servlet、Hello2Servlet、Hello3Servlet。通常大多数Servlet是在用户第一次请求的时候由应用服务器创建并初始化,但<load-on-startup>n</load-on-startup>   可以用来改变这种状况,根据自己需要改变加载的优先级!

  而且一个Servlet类型,服务器只创建一个实例对象,例如在我们首次访问http://localhost:8080/helloservlet/helloworld时,服务器通过“/helloworld”找到了绑定的Servlet名称为cn.itcast.servlet.HelloServlet,然后服务器查看这个类型的Servlet是否已经创建过,如果没有创建过,那么服务器才会通过反射来创建HelloServlet的实例。当我们再次访问http://localhost:8080/helloservlet/helloworld时,服务器就不会再次创建HelloServlet实例了,而是直接使用上次创建的实例。

因为一个类型的Servlet只有一个实例对象,那么就有可能会出现一个Servlet同时处理多个请求,那么Servlet是否为线程安全的呢?答案是:“不是线程安全的”。这说明Servlet的工作效率很高,但也存在线程安全问题!
所以我们不应该在Servlet中创建成员变量(可以创建局部变量),因为可能会存在一个线程对这个成员变量进行写操作,另一个线程对这个成员变量进行读操作

  在Servlet被创建后,服务器会马上调用Servlet的void init(ServletConfig)方法。请记住, Servlet出生后马上就会调用init()方法,而且一个Servlet的一生,这个方法只会被调用一次。这好比小孩子出生后马上就要去剪脐带一样,而且剪脐带一生只有一次。

  我们可以把一些对Servlet的初始化工作放到init方法中!

2.2 Servlet的服务

  当服务器每次接收到请求时,都会去调用Servlet的service()方法来处理请求。服务器接收到一次请求,就会调用service() 方法一次,所以service()方法是会被调用多次的。正因为如此,所以我们才需要把处理请求的代码写到service()方法中!

2.3 Servlet的销毁

  Servlet是不会轻易离去的,通常都是在服务器关闭时Servlet才会离去!在服务器被关闭时,服务器会去销毁Servlet,在销毁Servlet之前服务器会先去调用Servlet的destroy()方法,我们可以把Servlet的临终遗言放到destroy()方法中,例如对某些资源的释放等代码放到destroy()方法中。

三、Servlet的相关配置

3.1 基本配置

<servlet>
    <servlet-name>abc</servlet-name>
    <servlet-class>com.itheima.servlet.QuickStratServlet</servlet-class>
    <init-param>
      <param-name>url</param-name>
      <param-value>jdbc:mysql:///mydb</param-value>
    </init-param>
    <!--服务器启动时创建servlet-->
    <load-on-startup>3</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>abc</servlet-name>
    <url-pattern>/quickStratServlet</url-pattern>
  </servlet-mapping>

3.2 url-pattern的配置方式

  <url-pattern>是<servlet-mapping>的子元素,用来指定Servlet的访问路径,即URL。它必须是以“/”开头

【完全匹配】:访问的资源与配置的资源完全相同才能访问到

<url-pattern>/quickStratServlet</url-pattern>

  可以在<servlet-mapping>中给出多个<url-pattern>,例如:

 <servlet-mapping>
    <servlet-name>AServlet</servlet-name>
    <url-pattern>/AServlet</url-pattern>
    <url-pattern>/BServlet</url-pattern>
  </servlet-mapping>  

  那么这说明一个Servlet绑定了两个URL,无论访问/AServlet还是/BServlet,访问的都是AServlet。 

【目录匹配】:格式:/虚拟的目录../*   *代表任意

<url-pattern>/servlet/* <url-patter>:/servlet/a、/servlet/b,都匹配/servlet/*;

【 扩展名匹配】:格式:*.扩展名

<url-pattern>*.do </url-pattern>:/abc/def/ghi.do、/a.do,都匹配*.do;

  注意:第二种与第三种不要混用,通配符要么为前缀,要么为后缀,不能出现在URL中间位置,也不能只有通配符。例如:/*.do就是错误的,因为星号出现在URL的中间位置上了。*.*也是不对的,因为一个URL中最多只能出现一个通配符。

【缺省路径】/

  通常情况访问html页面时,首先从当前web项目的web.xml文件寻找匹配路径,如果没有找到,再从tomcat默认的web.xml匹配,将使用缺省servlet

<servlet>
    <servlet-name>hello1</servlet-name>
    <servlet-class>cn.itcast.servlet.Hello1Servlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>hello1</servlet-name>
    <url-pattern>/servlet/hello1</url-pattern>
</servlet-mapping>
<servlet>
    <servlet-name>hello2</servlet-name>
    <servlet-class>cn.itcast.servlet.Hello2Servlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>hello2</servlet-name>
    <url-pattern>/servlet/*</url-pattern>
</servlet-mapping>

  tomcat获得匹配l路径时,优先级顺序:完全路径匹配>目录匹配>扩展名匹配

  当访问路径为http://localhost:8080/hello/servlet/hello1时,因为访问路径即匹配hello1的<url-pattern>,又匹配hello2的<url-pattern>,但因为hello1的<url-pattern>中没有通配符,所以优先匹配,即设置hello1。

四、ServletConfig对象

 

4.1 ServletConfig的作用

  ServletConfig对象是由服务器创建的,然后传递给Servlet的init()方法。ServletConfig对象对应web.xml文件中的<servlet>元素。例如你想获取当前Servlet在web.xml文件中的配置名,那么可以使用servletConfig.getServletName()方法获取!

  ServletConfig对象主要是用于加载servlet的初始化参数,它可以获取<servlet>中配置的<init-param>信息

  在一个web应用可以存在多个ServletConfig对象(一个Servlet对应一个ServletConfig对象)

4.2 ServletConfig的API

  • String getServletName():获取Servlet在web.xml文件中的配置名称,即<servlet-name>指定的名称;

  • ServletContext getServletContext():用来获取ServletContext对象;

  • String getInitParameter(String name):用来获取在web.xml中配置的初始化参数,通过参数名<param-name>来获取参数<param-value>的值;

  • Enumeration getInitParameterNames():用来获取在web.xml中配置的所有初始化参数名称;

五、ServletContext对象

5.1 ServletContext概述

  一个项目只有一个ServletContext对象!我们可以在N多个Servlet中来获取这个唯一的对象,使用它可以给多个Servlet传递数据。

  WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用:  

  • ServletContext对象的创建是在服务器启动时完成的;

  • ServletContext对象的销毁是在服务器关闭时完成的。

  ServletContext对象的作用是在整个Web应用的动态资源之间共享数据!例如在AServlet中向ServletContext对象中保存一个值,然后在BServlet中就可以获取这个值,这就是共享数据了。ServletContext对象通常也被称之为context域对象。

5.2 获取ServletContext

  • 在Servlet中获取ServletContext对象:Servlet接口的void init(ServletConfig config)中:

    ServletContext context = config.getServletContext();,
  • 在GenericeServlet或HttpServlet中获取ServletContext对象:GenericServlet类有getServletContext()方法,所以可以直接使用this.getServletContext()来获取:
    ServletContext servletContext = this.getServletContext();

5.3 ServletContext的作用

【获得web应用全局的初始化参数】 

  Servlet也可以获取初始化参数,但它是局部的参数;也就是说,一个Servlet只能获取自己的初始化参数,不能获取别人的,即初始化参数只为一个Servlet准备!

  可以配置公共的初始化参数,为所有Servlet而用!这需要使用ServletContext才能使用!

  web.xml中配置初始化参数:

<context-param>
  <param-name>driver</param-name>
  <param-value>com.mysql.jdbc.Driver</param-value>
</context-param>

  通过context对象获得参数:

// 获得ServletContext对象
ServletContext context = this.getServletContext();
// 获得初始化参数
String initParameter = context.getInitParameter("driver");
System.out.println(initParameter);

【获得web应用中任何资源的绝对路径】

【在整个Web应用的动态资源之间共享数据】

  例如在AServlet中向ServletContext对象中保存一个值,然后在BServlet中获取这个值

【ServletContext是一个域对象】

  ServletContext是JavaWeb四大域对象之一(PageContext、ServletRequest、HttpSession、ServletContext)。

  域对象就是用来在多个Servlet中传递数据的,所有域对象都有存取数据的功能,因为域对象内部有一个Map,用来存储数据,下面是ServletContext对象用来操作数据的方法:

  • void setAttribute(String name, Object value):用来存储一个对象,也可以称之为存储一个域属性,例如:servletContext.setAttribute(“xxx”, “XXX”),在ServletContext中保存了一个域属性,域属性名称为xxx,域属性的值为XXX。请注意,如果多次调用该方法,并且使用相同的name,那么会覆盖上一次的值,这一特性与Map相同;

  • Object getAttribute(String name):用来获取ServletContext中的数据,当前在获取之前需要先去存储才行,例如:String value = (String)servletContext.getAttribute(“xxx”);,获取名为xxx的域属性;

  • void removeAttribute(String name):用来移除ServletContext中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;

  • Enumeration getAttributeNames():获取所有域属性的名称;

六、小案例——统计访问量

public class AServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 获取ServletContext对象
        ServletContext context = this.getServletContext();
        // 从ServletContext对象中获取名为count的属性
        Integer count = (Integer) context.getAttribute("count");
        if (count == null) {
            // 如果不存在:说明是第一次访问,向Servletcontext中保存名为count的属性,值为1
            context.setAttribute("count", 1);
        } else {
            // 如果存在:给访问量加1,然后再保存回去;
            context.setAttribute("count", count + 1);
        }
        // 向浏览器输出
        PrintWriter writer = response.getWriter();
        writer.print("<h1>" + count + "</h1>");
    }
}
原文地址:https://www.cnblogs.com/yft-javaNotes/p/10464529.html