1、Servlet概述

Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。

狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

最早支持Servlet标准的是JavaSoft的Java [Web Server](https://baike.baidu.com/item/Web Server/9306055?fromModule=lemma_inlink),此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。

JavaEE8官方API

2、Servlet 快速入门

要想实现Servlet功能,可以实现Servlet接口,继承GenericServlet或者HttpServlet

public class StudentServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("servlet 入门");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
<!--首页设置-->
<welcome-file-list>
    <welcome-file>/html/frame.html</welcome-file>
</welcome-file-list>
<!--servlet声明-->
<servlet>
    <servlet-name>studentServlet</servlet-name>
    <servlet-class>com.dft.servlet.StudentServlet</servlet-class>
</servlet>
<!--servlet映射-->
<servlet-mapping>
    <servlet-name>studentServlet</servlet-name>
    <url-pattern>/studentServlet</url-pattern>
</servlet-mapping>

3、Servlet的生命周期

出生:请求第一次到达Servlet时,对象就创建出来,并且初始化成功。只出生一次,就放到内存中。

活着:服务器提供服务的整个过程中,该对象一直存在,每次只是执行service方法。

死亡:当服务停止时,或者服务器宕机时,对象消亡。

4、Servlet的线程安全

由于Servlet运用了单例模式,即整个应用中只有一个实例对象,所以我们需要分析这个唯一的实例中的类成员是否线程安全。

/*
    Servlet线程安全
 */
public class ServletDemo04 extends HttpServlet{
    //1.定义用户名成员变量
    //private String username = null;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = null;
        //synchronized (this) {
            //2.获取用户名
            username = req.getParameter("username");

            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //3.获取输出流对象
            PrintWriter pw = resp.getWriter();

            //4.响应给客户端浏览器
            pw.print("welcome:" + username);

            //5.关流
            pw.close();
        //}
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

我们会发现用两个浏览器用不同的username分别访问,输出的username是一样的,可以得出结论Servlet是不安全的。

Servlet是单例,单例对象的类成员只会随类实例化时初始化一次,之后的操作都是改变,而不会重新初始化。

解决这个问题也非常简单,就是在Servlet中定义类成员要慎重。如果类成员是共用的,并且只会在初始化时赋值,其余时间都是获取的话,那么是没问题。如果类成员并非共用,或者每次使用都有可能对其赋值,那么就要考虑线程安全问题了,把它定义到doGet或者doPost方法里面去就可以了。

5、Servlet的映射方式

指名道姓的方式

访问的资源路径和映射配置完全相同

<!--servlet声明-->
<servlet>
    <servlet-name>studentServlet</servlet-name>
    <servlet-class>com.dft.servlet.StudentServlet</servlet-class>
</servlet>
<!--servlet映射-->
<servlet-mapping>
    <servlet-name>studentServlet</servlet-name>
    <url-pattern>/studentServlet</url-pattern>
</servlet-mapping>
开头+通配符的方式

只要符合目录结构即可,不用考虑结尾是什么。

映射为:/servlet/*

http://localhost:8585/servlet/itheima

通配符+固定格式结尾**

映射为:*.do

http://localhost:8585/servlet/itcast.do

Servlet支持多种配置方式,但是由此也引出了一个问题,当有两个及以上的Servlet映射都符合请求URL时,由谁来响应呢?注意:HTTP协议的特征是一请求一响应的规则。那么有一个请求,必然有且只有一个响应。所以,我们接下来明确一下,多种映射规则的优先级。

先说结论:指名道姓的方式优先级最高,带有通配符的映射方式,有/的比没/的优先级高

所以,我们前面讲解的三种映射方式的优先级为:第一种>第二种>第三种。

多路径映射Servlet

给一个Servlet配置多个访问映射,从而可以根据不同请求URL实现不同的功能。

场景:

public class ServletDemo7 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取当前请求的URI
        String uri = req.getRequestURI();
        uri = uri.substring(uri.lastIndexOf("/"),uri.length());
        //2.判断是1号请求还是2号请求
        if("/servletDemo7".equals(uri)){
            System.out.println("ServletDemo7执行1号请求的业务逻辑:商品单价7折显示");
        }else if("/demo7".equals(uri)){
            System.out.println("ServletDemo7执行2号请求的业务逻辑:商品单价8折显示");
        }else {
            System.out.println("ServletDemo7执行基本业务逻辑:商品单价原价显示");
        }
    }

}
<!--配置ServletDemo7-->
<servlet>
    <servlet-name>servletDemo7</servlet-name>
    <servlet-class>com.itheima.web.servlet.ServletDemo7</servlet-class>
</servlet>
<!--映射路径1-->
<servlet-mapping>
    <servlet-name>servletDemo7</servlet-name>
    <url-pattern>/demo7</url-pattern>
</servlet-mapping>
<!--映射路径2-->
<servlet-mapping>
    <servlet-name>servletDemo7</servlet-name>
    <url-pattern>/servletDemo7</url-pattern>
</servlet-mapping>
<!--映射路径3-->
<servlet-mapping>
    <servlet-name>servletDemo7</servlet-name>
    <url-pattern>/servlet/*</url-pattern>
</servlet-mapping>

Servlet 创建时机

Servlet的创建默认情况下是请求第一次到达Servlet时创建的。

Servlet是单例的,也就是说在应用中只有唯一的一个实例,所以在Tomcat启动加载应用的时候就创建也是一个很好的选择。

启动时加载

​ 应用加载时创建Servlet,它的优势是在服务器启动时,就把需要的对象都创建完成了,从而在使用的时候减少了创建对象的时间,提高了首次执行的效率。它的弊端也同样明显,因为在应用加载时就创建了Servlet对象,因此,导致内存中充斥着大量用不上的Servlet对象,造成了内存的浪费。

访问时加载

​ 请求第一次访问是创建Servlet,它的优势就是减少了对服务器内存的浪费,因为那些一直没有被访问过的Servlet对象都没有创建,因此也提高了服务器的启动时间。而它的弊端就是,如果有一些要在应用加载时就做的初始化操作,它都没法完成,从而要考虑其他技术实现。

在web.xml中是支持对Servlet的创建时机进行配置的

在web.xml中是支持对Servlet的创建时机进行配置的<!--配置ServletDemo3-->
<servlet>
    <servlet-name>servletDemo3</servlet-name>
    <servlet-class>com.itheima.web.servlet.ServletDemo3</servlet-class>
    <!--配置Servlet的创建顺序,当配置此标签时,Servlet就会改为应用加载时创建
        配置项的取值只能是正整数(包括0),数值越小,表明创建的优先级越高
    -->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>servletDemo3</servlet-name>
    <url-pattern>/servletDemo3</url-pattern>
</servlet-mapping>
默认Servlet

默认Servlet是由服务器提供的一个Servlet,它配置在Tomcat的conf目录下的web.xml中。

    <servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
        <init-param>
            <param-name>fork</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>xpoweredBy</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>3</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

它的映射路径是<url-pattern>/<url-pattern>,我们在发送请求时,首先会在我们应用中的web.xml中查找映射配置,找到就执行,当找不到对应的Servlet路径时,就去找默认的Servlet,由默认Servlet处理。

Servlet总图

22090201