Servlet方法详解

Servlet:

Servlet是SUN公司提供的一套规范,名称就叫Servlet规范,它也是JavaEE规范之一。使用JavaEE的API。目前在Oracle官网中的最新版本是JavaEE8,

  • Servlet是一个运行在web服务端的java小程序
  • 它可以用于接收和响应客户端的请求
  • 要想实现Servlet功能,可以实现Servlet接口,继承GenericServlet或者HttpServlet
  • 每次请求都会执行service方法
  • Servlet还支持配置
Servlet执行过程:
  1. 客户端浏览器发起请求
  2. Tomcat服务器解析URL
  3. 通过URL找到对应的应用
  4. 通过应用找到web.xml
  5. 解析请求资源地址URL
  6. 找到应用的资源
  7. 执行service方法,响应给客户端浏览器
Servlet关系视图:

在这里插入图片描述

Servlet实现方式:

1、 实现Servlet接口,接口中的方法必须全部实现。

表示接口中的所有方法在需求方面都有重写的必要。这种方式支持最大程度的自定义。

2、继承GenericServlet,service方法必须重写,其他方可根据需求,选择性重写。

表示只在接收和响应客户端请求这方面有重写的需求,而其他方法可根据实际需求选择性重写,使开发Servlet变得简单。但是,此种方式是和HTTP协议无关的。

3、继承HttpServlet,它是javax.servlet.http包下的一个抽象类,是GenericServlet的子类。如果选择继承HttpServlet时,只需要重写doGet和doPost方法,不要覆盖service方法。

表示我们的请求和响应需要和HTTP协议相关。也就是说通过HTTP协议来访问的。那么每次请求和响应都符合HTTP协议的规范。请求的方式就是HTTP协议所支持的方式

实现Servlet:
public class ServletDemo03 implements Servlet {

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("实现Servlet方式");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}
继承GenericServlet:
public class ServletDemo01 extends GenericServlet {

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service方法执行了...");
    }
}
继承HttpServlet::
public class ServletDemo 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 {
        doGet(req, resp);
    }
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--Servlet配置-->
    <servlet>
        <servlet-name>servletDemo</servlet-name>
        <servlet-class>com.example.demo.ServletDemo</servlet-class>
    </servlet>

    <!--映射配置-->
    <servlet-mapping>
        <servlet-name>servletDemo</servlet-name>
        <url-pattern>/servletDemo</url-pattern>
    </servlet-mapping>


    <!--生命周期配置-->
    <servlet>
        <servlet-name>servletDemo02</servlet-name>
        <servlet-class>com.example.demo.ServletDemo02</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>servletDemo02</servlet-name>
        <url-pattern>/servletDemo02</url-pattern>
    </servlet-mapping>
    
</web-app>
Servlet生命周期:
  • 对象的生命周期,就是对象从生到死的过程,
  • 出生:请求第一次到达Servlet时,对象就创建出来,并且初始化成功。只出生一次,就放到内存中。
  • 活着:服务器提供服务的整个过程中,该对象一直存在,每次都是执行service方法。
  • 死亡:当服务停止时,或者服务器宕机时,对象消亡。
  • Servlet对象只会创建一次,销毁一次。所以,Servlet对象只有一个实例。如果一个对象实例在应用中是唯一的存在,用了单例模式。

演示:

public class ServletDemo02 extends HttpServlet {

    // 对象出生
    @Override
    public void init() throws ServletException {
        System.out.println("出生了");
    }

    // 对象服务

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("收到客户端请求");
    }

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

    // 对象死亡
    @Override
    public void destroy() {
        System.out.println("挂了");
    }
}
线程安全:

在Servlet中定义了类成员之后,多个浏览器都会共享类成员的数据。其实每一个浏览器端发送请求,就代表是一个线程,那么多个浏览器就是多个线程,多个线程会共享Servlet类成员中的数据,其中任何一个线程修改了数据,都会影响其他线程。因此,所以Servlet它不是线程安全的。分析产生这个问题的根本原因,其实就是因为Servlet是单例,单例对象的类成员只会随类实例化时初始化一次,之后的操作都是改变,而不会重新初始化。

解决:

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

演示:

public class ServletDemo03 extends HttpServlet {

    private String username;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        synchronized (this) {
            // 在这里声明的话,就是把成员变量变成局部变量,谷歌进来是一个username,火狐进来又是另一个就不会出现出数据不安全了
            // String username = null;
            // String getParameter(String name):根据参数名称获取参数值
            username = req.getParameter("username");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // resp.getWriter  传数据给前端
            PrintWriter writer = resp.getWriter();
            writer.println("username:" + username);
            writer.close();
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
Servlet映射三种方式:
  1. 指名道姓,给具体的名称,访问路径必须和配置的一样
  2. /开头+通配符的方式,只要符合目录结构,无视结尾
  3. 通配符+固定格式结尾方式,只要符合固定结尾,无视前面路径

说明:映射优先级:越具体优先级越高,1>2>3

演示:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">


    <!--
    &lt;!&ndash;ServletHttp配置&ndash;&gt;
    <servlet>
        <servlet-name>servletDemo</servlet-name>
        <servlet-class>com.example.demo.ServletDemo</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>servletDemo</servlet-name>
        <url-pattern>/servletDemo</url-pattern>
    </servlet-mapping>
    -->

    <!--    &lt;!&ndash;/开头+通配符的方式&ndash;&gt;
        <servlet>
            <servlet-name>servletDemo</servlet-name>
            <servlet-class>com.example.demo.ServletDemo</servlet-class>
        </servlet>

        <servlet-mapping>
            <servlet-name>servletDemo</servlet-name>
            <url-pattern>/servlet/*</url-pattern>
        </servlet-mapping>-->

    <!--通配符+固定格式结尾  结尾写什么就写什么,开心就好-->
    <servlet>
        <servlet-name>servletDemo</servlet-name>
        <servlet-class>com.example.demo.ServletDemo</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>servletDemo</servlet-name>
        <url-pattern>*.itzhuzhu</url-pattern>
    </servlet-mapping>
</web-app>

实现类:

public class ServletDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 定义一个商品金额
        int money = 1000;
        // 获取访问路径
        String uri = req.getRequestURI();
        // public int lastIndexOf(int ch): 返回指定字符在此字符串中最后一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
        uri = uri.substring(uri.lastIndexOf("/"));
        // 条件判断
        if ("/vip".equals(uri)) {
            System.out.println("原价:" + money + "尊敬的Vip您的折后价是:" + money * 0.8);
        } else if ("/svip".equals(uri)) {
            System.out.println("原价:" + money + "尊敬的Sip您的折后价是:" + money * 0.1);
        } else {
            System.out.println("会员都不开还想打折?打骨折");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
Servlet创建时机:

第一种:服务器加载时创建

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

第二种:第一次访问时创建

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

标签里的数字越小优先级越高,正整数代表服务器加载时创建,负数不写代表第一次访问的时候创建

    <servlet>
        <servlet-name>servletDemo</servlet-name>
        <servlet-class>com.example.demo.ServletDemo</servlet-class>
        <!--数字越小越高,负数或者不写就是第一次访问的时候创建-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletDemo</servlet-name>
        <url-pattern>/servletDemo</url-pattern>
    </servlet-mapping>
默认Servlet:

默认Servlet是由服务器提供的一个Servlet,它配置在Tomcat的conf目录下web.xml中。它的映射路径是<url-pattern>/<url-pattern>,在发送请求时,首先会在应用中的web.xml中查找映射配置,找到就执行。找不到对应的Servlet路径时,就去找默认的Servlet,由默认Servlet处理。

ServletConfig:

ServletConfig是Servlet的配置参数对象,在Servlet规范中,允许为每个Servlet都提供一些初始化配置。所以,每个Servlet都一个自己的ServletConfig。它的作用是在Servlet初始化期间,把一些配置信息传递给Servlet。比如servlet是个孩子,ServletConfig就是保姆

生命周期:

由于它是在初始化阶段读取了web.xml中为Servlet准备的初始化配置,并把配置信息传递给Servlet,所以生命周期与Servlet相同。需要注意的是,如果Servlet配置了1,那么ServletConfig也会在应用加载时创建。

配置ServletConfig:

<servlet>标签中,通过<init-param>标签配置,有两个子标签
<param-name>:代表初始化参数的key
<param-value>:代表初始化参数的value

ServletConfig方法:
返回值方法名说明
StringgetInitParameter(String name)根据参数名称获取参数的值
EnumerationgetInitParameterNames()获取所有参数名称的枚举
StringgetServletName获取Servlet的名称
SerlvetContextgetServletContext获取ServletContext对象
演示:

配置信息:

    <servlet>
        <servlet-name>servletDemo05</servlet-name>
        <servlet-class>com.example.demo.ServletDemo05</servlet-class>
        <!--配置ServletConfig,是以键值对的形式存在的-->
        <init-param>
            <param-name>itz</param-name>
            <param-value>itzhuzhu</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>servletDemo05</servlet-name>
        <url-pattern>/servletDemo05</url-pattern>
    </servlet-mapping>

实现类:

public class servletDemo04 extends HttpServlet {
    // 声明ServletConfig
    private ServletConfig config;

    // 因为ServletConfig是在初始化期间传递信息,所以要在init方法里去获取
    @Override
    public void init(ServletConfig config) throws ServletException {
        // 通过init方法,对ServletConfig对象赋值
        this.config = config;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // getInitParameter(String name):根据key获取value
        String encodingValue = config.getInitParameter("encoding");
        System.out.println(encodingValue);

        // getInitParameterNames():获取所有key
        Enumeration<String> keys = config.getInitParameterNames();
        while (keys.hasMoreElements()) {
            // 获取每一个key
            String key = keys.nextElement();
            // 根据key获取value
            String value = config.getInitParameter(key);
            System.out.println(key + " : " + value);
        }

        // getServletName	获取Servlet的名称
        String servletName = config.getServletName();
        System.out.println(servletName);

        // getServletContext	获取ServletContext对象
        ServletContext servletContext = config.getServletContext();
        System.out.println(servletContext);
    }


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

ServletContext对象,它是应用上下文对象。每一个应用有且只有一个ServletContext对象。

作用:

它可以配置和获取应用的全局初始化参数,实现所有Servlet之间的数据共享。

生命周期:
  • 出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象。(Servlet和ServletContext都-是单例的)
  • 活着:只要应用一直提供服务,对象就一直存在。
  • 死亡:应用被卸载(或者服务器挂了),对象死亡。
域对象概念:
  • 域对象:指的是对象有作用域(作用范围)
  • 域对象的作用:域对象可以实现数据共享。
  • 不同作用范围的域对象,共享数据的能力不一样。在Servlet规范中,一共有4个域对象。ServletContext就是其中一个。也是web应用中最大的作用域,叫application域。每个应用只有一个application域(应用域),它可以实现整个应用间的数据共享功能。
ServletContext配置:

ServletContext被称之为应用上下文对象,所以它的配置是针对整个应用的配置,而非某个特定Servlet的配置。它的配置被称为应用的初始化参数配置。

配置的方式,需要在标签中使用来配置初始化参数

<param-name>:全局初始化参数的key
<param-value>:全局初始化参数的value

常用方法:
返回值方法名说明
StringgetInitParameter(String name)根据名称获取全局配置的参数
StringgetContextPath()获取当前应用访问的虚拟目录
StringgetRealPath根据虚拟目录获取应用部署的磁盘绝对路径
应用域常用方法:
返回值方法名说明
voidsetAttribute(String name,Object value)向应用域对象中存储数据
ObjectgetAttribute(String name)通过名称获取应用域对象中的数组
voidremoveAttribute(String name)通过名称移除应用域对象中的数据

配置文件:

<!--因为servletContext它不属于某一个servlet,它是配置全局的,所以不能写在servlet里-->
    <context-param>
        <param-name>desc</param-name>
        <param-value>This is Ser vletContextDemo</param-value>
    </context-param>

    <servlet>
        <servlet-name>servletContextDemo</servlet-name>
        <servlet-class>com.example.demo.ServletContextDemo</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>servletContextDemo</servlet-name>
        <url-pattern>/servletContextDemo</url-pattern>
    </servlet-mapping>

实现类:

public class ServletContextDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取ServletContext对象 , GenericServlet底层已经实现了getInitParameter所以可以直接调用  ServletConfig sc = this.getServletConfig();
        ServletContext context = getServletContext();

        //获取全局配置参数的desc
        String value = context.getInitParameter("desc");
        System.out.println(value);

        //获取应用的访问虚拟目录
        String contextPath = context.getContextPath();
        System.out.println(contextPath);

        //根据虚拟目录获取应用部署的磁盘绝对路径
        String realPath = context.getRealPath("/");
        System.out.println(realPath);

        // 在src、wabapp、webinf创建三个文件获取路径
        //获取a.txt文件的绝对路径
        String a = context.getRealPath("/WEB-INF/classes/a.txt");
        System.out.println(a);

        //获取b.txt文件的绝对路径
        String b = context.getRealPath("/b.txt");
        System.out.println(b);

        String c = context.getRealPath("/WEB-INF/c.txt");
        System.out.println(c);

        //向域对象中存储数据
        context.setAttribute("username", "itzhuzhu");

        //移除域对象中username的数据,删除了再访问就变成null了
        context.removeAttribute("username");
    }

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

获取共享数据:

public class ServletDemo05 extends HttpServlet {
    // 声明ServletConfig
    private ServletConfig config;

    // 因为ServletConfig是在初始化期间传递信息,所以要在init方法里去获取
    @Override
    public void init(ServletConfig config) throws ServletException {
        // 通过init方法,对ServletConfig对象赋值
        this.config = config;
    }

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

        // getServletContext	获取ServletContext对象
        ServletContext servletContext = config.getServletContext();
        System.out.println(servletContext);

        // getAttribute(String name)	通过名称获取应用域对象中的数组
        Object username = servletContext.getAttribute("username");
        System.out.println(username);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
Request方法详解:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

itzhuzhu.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值