Java Web篇_04 Servlet


Servlet

Servlet介绍

我们打开官网API可以了解到Servlet接口,他在javax.servlet.Servlet里。
在这里插入图片描述
它这里说:
servlet是在Web服务器中运行的小型Java程序。servlet通常通过超文本传输协议HTTP接收和响应来自Web客户端的请求。
要实现这个接口,您可以编写一个扩展javax.servlet.GenericServlet的通用servlet或一个扩展javax.servlet.http.HttpServlet的HTTP servlet。
该接口定义了初始化servlet、服务请求和从服务器删除servlet的方法。这些方法被称为生命周期方法,并按以下顺序调用:
1.构造servlet,然后使用init方法进行初始化。
2.处理客户端对服务方法的任何调用。
3.servlet被从服务中取出,然后使用destroy方法销毁,然后进行垃圾收集并最终完成。
除了生命周期方法之外,该接口还提供了getServletConfig方法(servlet可以使用该方法获取任何启动信息)和getServletInfo方法(允许servlet返回关于自身的基本信息,如作者、版本和版权)。(中文式扣脚英语,见笑了,最后还是用了翻译软件)
总结一下,就是:
第一:Servlet是一个运行在web服务端的java小程序
第二:它可以用于接收和响应客户端的请求
第三:要想实现Servlet功能,可以实现Servlet接口继承GenericServlet或者HttpServlet
第四:每次请求都会执行service方法

Servlet快速入门

第一步,先创建一个javaweb工程。
第二步,在src下建包,编写一个普通类继承GenericServlet并重写service方法。(之前发布web项目的例子用的是实现Servlet接口的方式,所以这里就不用那个了,继承GenericServlet方法即可)
我们打开API,会发现这个类只有一个抽象方法service
我们
所以我们只需要重写这一个方法就可以了。

public class ServletDemo1 extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service方法执行了");
    }
}

在这里我们发现参数列表里有两个参数,看名字就知道一个是响应的对象,一个是请求的对象。
第三步:在web.xml配置Servlet
注意类的路径是你自己定义的。

<!--    Servlet声明-->
    <servlet>
        <servlet-name>servletDemo1</servlet-name>
        <servlet-class>com.symc.servlet.ServletDemo1</servlet-class>
    </servlet>
<!--    Servlet映射-->
    <servlet-mapping>
        <servlet-name>servletDemo1</servlet-name>
        <url-pattern>/servletDemo1</url-pattern>
    </servlet-mapping>

第四步:测试
配置并核对一下Run Configurations,打开tomcat,在打开的地址栏里面加上你的映射名称
在这里插入图片描述
多次刷新并观察控制台
在这里插入图片描述

Servlet执行过程

我们通过浏览器发送请求,请求首先到达Tomcat服务器,由服务器解析请求URL,然后在部署的应用列表中找到我们的应用,也就是我们当前的项目。接下来,在我们的应用中找应用里的web.xml配置文件,在web.xml中找到FirstServlet的配置,(具体是这样,它是先找到中的 的中的地址,然后知道了映射中的名字,再通过映射名字找到声明中的名字,最后到了路径中的类),找到后执行service方法,最后由FirstServlet响应客户浏览器。整个过程如下图所示:
在这里插入图片描述
一句话总结执行过程:

浏览器请求——>Tomcat服务器——>我们的应用——>应用中的web.xml——>FirstServlet——>响应浏览器

Servlet关系视图

首先我们要明确的是Servlet这个总接口,而GenericServlet方法实现了Servlet接口,它是一个抽象类,只有service方法没有实现。而HttpServlet又继承了GenericServlet。service方法的形参列表中的ServletRequest和ServletResponse的对象,这两个是继承于Servlet的接口。与他们不一样的是,HttpServlet的service方法中是HttpServletRequest和HttpServletResponse,这两个接口又是分别继承于ServletRequest和ServletResponse。
在这里插入图片描述

Servlet编写方式

1)编写方式说明
这里总结一下编写方式。
我们在实现Servlet功能时,可以选择以下三种方式:
第一种:实现Servlet接口,接口中的方法必须全部实现。
​ 使用此种方式,表示接口中的所有方法在需求方面都有重写的必要。此种方式支持最大程度的自定义。

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

第三种:继承HttpServlet, 它是javax.servlet.http包下的一个抽象类,是GenericServlet的子类。如果我们选择继承HttpServlet时,只需要重写doGet和doPost方法,不要覆盖service方法。
​ 使用此种方式,表示我们的请求和响应需要和HTTP协议相关。也就是说,我们是通过HTTP协议来访问的。那么每次请求和响应都符合HTTP协议的规范。请求的方式就是HTTP协议所支持的方式(目前我们只知道GET和POST,而实际HTTP协议支持7种请求方式,GET POST PUT DELETE TRACE OPTIONS HEAD )。

前两种方式前面都有举例演示过了,这里只演示这继承HttpServlet的方式。
创建一个类演示一下:

public class ServletDemo2 extends HttpServlet {
    @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);
    }
}

我们要知道这种方式只需要重写doGet和doPost这两个方法即可,这里只需要写一个,另一个直接调用写好的那个。
那为什么不去写service方法了呢?我们打开service源码来看一下:

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest)req;
            response = (HttpServletResponse)res;
        } catch (ClassCastException var6) {
            throw new ServletException(lStrings.getString("http.non_http"));
        }

        this.service(request, response);
    }

上面的源码,我们要了解的是它将形参列表给转成HttpServletRequest和HttpServletResponse,也是实现servlet接口的直接方法,而下面的新添加的serivce方法是根本

 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        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);
        }

    }

很明显这里有7个实现方式,我们暂时了解最常用的get和post方式即可。

在这里创建好后,同样方式的配置,需要在web.xml文件中添加声明和映射。
在这里插入图片描述
最后到浏览器里访问一下看看,观察控制台的记录。

Servlet的生命周期

对象的生命周期,就是对象从生到死的过程,即:出生——活着——死亡。用更偏向于开发的官方说法就是对象创建到销毁的过程

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

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

死亡:当服务停止时,或者服务器宕机时,对象消亡
实例分析:
这里我们还要重写以下init和destroy方法

public class ServletDemo3 extends GenericServlet {
    @Override
    public void init() throws ServletException {
        System.out.println("Servlet对象创建并初始化");
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("客户端发起了一次请求");
    }

    @Override
    public void destroy() {
        System.out.println("对象销毁");
    }
}

我们打开tomcat,浏览器访问一次,观察控制台
在这里插入图片描述
再刷新几次页面,发现控制台只显示创建这一次对象。
在这里插入图片描述
现在关闭tomcat,发现对象销毁了。
在这里插入图片描述
通过分析Servlet的生命周期我们发现,它的实例化和初始化只会在请求第一次到达Servlet时执行,而销毁只会在Tomcat服务器停止时执行,由此我们得出一个结论,Servlet对象只会创建一次,销毁一次。所以,Servlet对象只有一个实例。如果一个对象实例在应用中是唯一的存在,那么我们就说它是单实例的,即运用了单例模式。

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);
    }
}

配置,启动服务,启动两个浏览器,输入不同的参数,(我这里一个输入啊aaa,另一个bbb)访问之后发现输出的结果都是一样,所以出现线程安全问题。
在这里插入图片描述
通过上面的测试我们发现,在Servlet中定义了类成员之后,多个浏览器都会共享类成员的数据。其实每一个浏览器端发送请求,就代表是一个线程,那么多个浏览器就是多个线程,所以测试的结果说明了多个线程会共享Servlet类成员中的数据,其中任何一个线程修改了数据,都会影响其他线程。因此,我们可以认为Servlet它不是线程安全的。

分析产生这个问题的根本原因,其实就是因为Servlet是单例,单例对象的类成员只会随类实例化时初始化一次,之后的操作都是改变,而不会重新初始化。

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

Servlet映射方式

Servlet支持三种映射方式,以达到灵活配置的目的。

首先编写一个Servlet,代码如下:

public class ServletDemo5 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("ServletDemo5接收到了请求");
    }

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

第一种:指名道姓的方式

​ 此种方式,只有和映射配置一模一样时,Servlet才会接收和响应来自客户端的请求。
三种方式的区别代码都是 <url -pattern>标签中的内容

<!--    指定名称的映射方式-->
    <servlet>
        <servlet-name>servletDemo5</servlet-name>
        <servlet-class>com.symc.servlet.ServletDemo5</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletDemo5</servlet-name>
        <url-pattern>/servletDemo5</url-pattern>
    </servlet-mapping>

第二种:/开头+通配符的方式
​ 此种方式,只要符合目录结构即可,不用考虑结尾是什么。

<!--    /开头+通配符-->
    <servlet>
        <servlet-name>servletDemo5</servlet-name>
        <servlet-class>com.symc.servlet.ServletDemo5</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletDemo5</servlet-name>
        <url-pattern>/servlet/*</url-pattern>
    </servlet-mapping>

那个*代表url它对应的位置随意起名。
第三种:通配符+固定格式结尾

​ 此种方式,只要符合固定结尾格式即可,其前面的访问URI无须关心(注意协议,主机和端口必须正确)

​ 例如:映射为: *.do
只要是以 .do作为结尾,而.前面任意,都可以成功访问到。

    <servlet>
        <servlet-name>servletDemo5</servlet-name>
        <servlet-class>com.symc.servlet.ServletDemo5</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletDemo5</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

通过测试我们发现,Servlet支持多种配置方式,但是由此也引出了一个问题,当有两个及以上的Servlet映射都符合请求URL时,由谁来响应呢?注意:HTTP协议的特征是一请求一响应的规则。那么有一个请求,必然有且只有一个响应。所以,我们接下来明确一下,多种映射规则的优先级。
先说结论:指名道姓的方式优先级最高,带有通配符的映射方式,有/的比没/的优先级高
所以,我们前面讲解的三种映射方式的优先级为:第一种>第二种>第三种。

多路径映射Servlet

上面我们讲解了Servlet的多种映射方式,现在我们来介绍一下,一个Servlet的多种路径配置的支持。

它其实就是给一个Servlet配置多个访问映射,从而可以根据不同请求URL实现不同的功能。
来看一个实例:
首先,创建一个Servlet:

package com.symc.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: 凤文  沈阳医学院2019级医学信息工程 0213
 * @CreateTime: 2021/10/08 21:27
 * @Description: 商品打折,会员身份访问不同路径的情况
 */
public class ServletDemo6 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取当前请求的URI
        int money = 1000;
        String path = req.getRequestURI();
        path = path.substring(path.lastIndexOf("/"));//截取字符串
        //2.判断请求
        if ("/vip".equals(path)) {
            System.out.println("商品原价为:"+ money + "。优惠后为"+ 0.9*money);
        }else if("/svip".equals(path)){
            System.out.println("商品原价为:"+ money + "。优惠后为"+ 0.7*money);
        }else{
            System.out.println("商品原价为:"+ money);
        }
    }

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

配置映射

    <servlet>
        <servlet-name>servletDemo6</servlet-name>
        <servlet-class>com.symc.servlet.ServletDemo6</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletDemo6</servlet-name>
        <url-pattern>/sale/*</url-pattern>
    </servlet-mapping>

按照这三种不同方式访问
在这里插入图片描述
依次观察控制台
在这里插入图片描述

Servlet创建时机

我们前面讲解了Servlet的生命周期,Servlet的创建默认情况下是请求第一次到达Servlet时创建的。但是我们都知道,Servlet是单例的,也就是说在应用中只有唯一的一个实例,所以在Tomcat启动加载应用的时候就创建也是一个很好的选择。那么两者有什么区别呢?

第一种:应用加载时创建Servlet,它的优势是在服务器启动时,就把需要的对象都创建完成了,从而在使用的时候减少了创建对象的时间,提高了首次执行的效率。它的弊端也同样明显,因为在应用加载时就创建了Servlet对象,因此,导致内存中充斥着大量用不上的Servlet对象,造成了内存的浪费。
第二种:请求第一次访问时创建Servlet,它的优势就是减少了对服务器内存的浪费,因为那些一直没有被访问过的Servlet对象都没有创建,因此也提高了服务器的启动时间。而它的弊端就是,如果有一些要在应用加载时就做的初始化操作,它都没法完成,从而要考虑其他技术实现。

就是当需要在应用加载就要完成一些工作时,就需要选择第一种方式。当有很多Servlet的使用时机并不确定时,就选择第二种方式。

上面的例子使用的都是第二种方式,这里演示一下第一种方式。在web.xml中配置的方式如下:

<!--配置ServletDemo3-->
<servlet>
    <servlet-name>servletDemo3</servlet-name>
    <servlet-class>com.symc.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>

这里不需要访问,启动tomcat就创建好了。
在这里插入图片描述

默认Servlet

默认Servlet是由服务器提供的一个Servlet,它配置在Tomcat的conf目录下的web.xml中。如下图所示:
在这里插入图片描述
它的映射路径是/,我们在发送请求时,首先会在我们应用中的web.xml中查找映射配置,找到就执行,这块没有问题。但是当找不到对应的Servlet路径时,就去找默认的Servlet,由默认Servlet处理。所以,一切都是Servlet。

ServletConfig

ServletConfig介绍

基本概念
它是Servlet的配置参数对象,在Servlet规范中,允许为每个Servlet都提供一些初始化配置。所以,每个Servlet都一个自己的ServletConfig。它的作用是在Servlet初始化期间,把一些配置信息传递给Servlet。

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

ServletConfig配置方式

在标签中,通过标签来配置。有两个子标签。
代表初始化参数的key。
代表初始化参数的value。
在演示下面的代码前先创建一个类ServletConfigDemo

    <servlet>
        <servlet-name>servletConfigDemo</servlet-name>
        <servlet-class>com.symc.servlet.ServletConfigDemo</servlet-class>
<!--        配置servletConfig-->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>desc</param-name>
            <param-value>This is ServletConfig</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletConfigDemo</servlet-name>
        <url-pattern>/servletConfigDemo</url-pattern>
    </servlet-mapping>

ServletConfig常用方法

在这里插入图片描述

package com.symc.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;

/**
 * @Author: 凤文  沈阳医学院2019级医学信息工程 0213
 * @CreateTime: 2021/10/10 14:29
 * @Description: ServletConfig 的 演示
 */
public class ServletConfigDemo extends HttpServlet {
    // 1.声明ServletConfig
    private ServletConfig servletConfig;

    // 2.通过init()方法来对ServletConfig对象进行赋值。
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        this.servletConfig = servletConfig;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 3.演示ServletConfig常用的方法
        //根据key获取value
        String encodingValue = servletConfig.getInitParameter("encoding");
        System.out.println(encodingValue);
        //获取所有的key
        Enumeration<String> names = servletConfig.getInitParameterNames();
        while (names.hasMoreElements()){
            String key = names.nextElement();
            String value = servletConfig.getInitParameter(key);
            System.out.println(key + ":" + value);
        }
        // 4.获取Servlet的名称
        String servletName = servletConfig.getServletName();
        System.out.println(servletName);
        // 5.获取ServletContext对象
        ServletContext servletContext = servletConfig.getServletContext();
        System.out.println(servletContext);
    }

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

配置tomcat,打开服务器,浏览器访问,控制台情况如下:
在这里插入图片描述

ServletContext

ServletContext介绍

ServletContext对象,它是应用上下文对象。每一个应用有且只有一个ServletContext对象。它可以实现让应用中所有Servlet间的数据共享
生命周期
出生——活着——死亡
出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象。(Servlet和ServletContext都是单例的)
活着:只要应用一直提供服务,该对象就一直存在。
死亡:应用被卸载(或者服务器挂了),该对象消亡。

域对象概念
域对象的概念,它指的是对象有作用域,即有作用范围。
域对象的作用,域对象可以实现数据共享。不同作用范围的域对象,共享数据的能力不一样。
在Servlet规范中,一共有4个域对象。我们的ServletContext就是其中一个。它也是我们接触的第一个域对象。它是web应用中最大的作用域,叫application域。每个应用只有一个application域。它可以实现整个应用间的数据共享功能。

ServletContext配置

ServletContext既然被称之为应用上下文对象,所以它的配置是针对整个应用的配置,而非某个特定Servlet的配置。它的配置被称为应用的初始化参数配置。
配置的方式,需要在标签中使用来配置初始化参数。具体代码如下:

    <servlet>
        <servlet-name>servletContextDemo</servlet-name>
        <servlet-class>com.symc.servlet.ServletContextDemo</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletContextDemo</servlet-name>
        <url-pattern>/servletContextDemo</url-pattern>
    </servlet-mapping>
    <!--配置应用初始化参数-->
    <context-param>
        <!--用于获取初始化参数的key-->
        <param-name>servletContextInfo</param-name>
        <!--初始化参数的值-->
        <param-value>This is application scope</param-value>
    </context-param>
    <!--每个应用初始化参数都需要用到context-param标签-->
    <context-param>
        <param-name>globalEncoding</param-name>
        <param-value>UTF-8</param-value>
    </context-param>

需要注意的是,这里标签是与servlet标签并列的,而不是config那样包含的关系。

ServletContext常用方法

在这里插入图片描述
对于获取应用部署的绝对路径,有三个位置,分别是src目录下,web和WEB-INF下,为了演示,我们分别在他们下面建立a.txt,b.txt,c.txt文件。

我们打开文件会发现在web目录下创建的b.txt文件在web_demo3\out\artifacts\ web_demo4_war_exploded
在这里插入图片描述
在WEB-INF下创建的c.txt文件在web_demo3\out\artifacts\web_demo4_war_exploded\ WEB-INF
在这里插入图片描述
而在src目录下创建的a.txt文件在web_demo3\out\artifacts\web_demo4_war_exploded\WEB-INF\ classes
在这里插入图片描述
代码放到下面一起看。

还有一些有关数据共享的常用方法。
在这里插入图片描述
首先创建一个ServletContextDemo对象

package com.symc.servlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: 凤文  沈阳医学院2019级医学信息工程 0213
 * @CreateTime: 2021/10/10 15:53
 * @Description:
 */
public class ServletContextDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取ServletContext对象
        ServletContext servletContext = getServletContext();
        //获取全局配置的globalEncoding
        String globalEncoding = servletContext.getInitParameter("globalEncoding");
        System.out.println(globalEncoding);
        //获取应用的访问虚拟目录
        String contextPath = servletContext.getContextPath();
        System.out.println(contextPath);
        //根据虚拟目录获取应用部署的磁盘绝对路径
        String realPath = servletContext.getRealPath(contextPath);
        System.out.println(realPath);
        //获取b.txt文件的绝对路径
        String realPath1 = servletContext.getRealPath("/b.txt");
        System.out.println(realPath1);
        //获取c.txt文件的绝对路径
        String realPath2 = servletContext.getRealPath("/c.txt");
        System.out.println(realPath2);
        //获取a.txt文件的绝对路径
        String realPath3 = servletContext.getRealPath("/a.txt");
        System.out.println(realPath3);
        //向域对象中存储数据
        servletContext.setAttribute("username","zhangsan");
        //移除域对象中username的数据
        //context.removeAttribute("username");
    }

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

在共享数据这里,设置好后,到之前的ServletConfigDemo中测试一下
在doGet代码中添加

        // 5.获取ServletContext对象
        ServletContext servletContext = servletConfig.getServletContext();
        System.out.println(servletContext);
        // 获取共享数据
        Object value = servletContext.getAttribute("username");
        System.out.println(value);

最后启动服务器测试。

注解开发Servlet

Servlet3.0规范

首先,我们要明确一件事情,我们使用的是Tomcat9,JavaEE规范要求是8,对应的Servlet规范规范应该是JavaEE8包含的4.x版本。

但是,在企业级应用的开发中,稳定远比追新版本重要的多。所以,我们虽然用到了Tomcat9和对应的JavaEE8,但是涉及的Servlet规范我们降版使用,用的是Servlet3.1版本。关于兼容性问题也无须担心,向下兼容的特性,在这里也依然适用。

我们使用的是Servlet3.1版本的规范,但这里所提的是Servlet3.0规范,这两个到底有怎样的联系呢?
在大概十多年前,那会还是Servlet2.5的版本的天下,它最明显的特征就是Servlet的配置要求配在web.xml中,在这部分内容之前,全都是基于Servlet2.5规范编写的。从2007年开始到2009年底,在这个时间段,软件开发开始逐步的演变,基于注解的配置理念开始逐渐出现,大量注解配置思想开始用于各种框架的设计中,例如:Spring3.0版本的Java Based Configuration,JPA规范,apache旗下的struts2和mybatis的注解配置开发等等。

JavaEE6规范也是在这个期间设计并推出的,与之对应就是它里面包含了新的Servlet规范:Servlet3.0版本!

自动注解配置(案例)

这次我们创建一个Javaweb项目时,选择JavaEE8 的版本,下面的Web Application依旧要勾选,对于最下面的版本新的是4.0的版本,对于企业来讲用的大多都是3.1的版本,我这里用最新的。
在这里插入图片描述
创建好之后的目录如下,这里的web.xml就没有用处了,可以移除,如果使用3.1版本的没有WEB-INF需要手动创建一个。
在这里插入图片描述
编写Servlet,使用注解配置Servlet。使用注解的话,这样就不用再去配置xml文件了,做法很清爽,所以对于xml的方式可以不去用了。

package com.symc.servlet;

import com.sun.media.sound.SoftTuning;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: 凤文  沈阳医学院2019级医学信息工程 0213
 * @CreateTime: 2021/10/10 17:46
 * @Description: 基于注解方式开发Servlet
 * 需要注意的是,注解是写在类的上面
 */
@WebServlet("/servletDemo1")
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);
    }
}

注解详解

/**
 * WebServlet注解
 * @since Servlet 3.0 (Section 8.1.1)
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {

    /**
     * 指定Servlet的名称。
     * 相当于xml配置中<servlet>标签下的<servlet-name>
     */
    String name() default "";

    /**
     * 用于映射Servlet访问的url映射
     * 相当于xml配置时的<url-pattern>
     */
    String[] value() default {};

    /**
     * 相当于xml配置时的<url-pattern>
     */
    String[] urlPatterns() default {};

    /**
     * 用于配置Servlet的启动时机
     * 相当于xml配置的<load-on-startup>
     */
    int loadOnStartup() default -1;

    /**
     * 用于配置Servlet的初始化参数
     * 相当于xml配置的<init-param>
     */
    WebInitParam[] initParams() default {};

    /**
     * 用于配置Servlet是否支持异步
     * 相当于xml配置的<async-supported>
     */
    boolean asyncSupported() default false;

    /**
     * 用于指定Servlet的小图标
     */
    String smallIcon() default "";

    /**
     * 用于指定Servlet的大图标
     */
    String largeIcon() default "";

    /**
     * 用于指定Servlet的描述信息
     */
    String description() default "";

    /**
     * 用于指定Servlet的显示名称
     */
    String displayName() default "";
}

案例

实现步骤

  1. 创建一个web项目。web_case_sys
  2. 创建一个用于保存学生信息的html文件。addStudent.html
  3. 创建一个类,继承HttpServlet。SudentServlet.java
  4. 重写doGet和doPost方法
  5. 在web.xml文件中修改默认主页和配置Servlet。
  6. 在doGet方法中接收到表单数据保存到文件中,并响应给浏览器结果。
  7. 部署并启动项目。
  8. 通过浏览器测试。

addStudent.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>保存学生信息</title>
</head>
<body>
    <div>
        <form action="/stu/studentServlet" method="get" autocomplete="off">
            学生姓名:<input type="text" name="name"><br>
            学生班级:<input type="number" name="class"><br>
            学生成绩:<input type="number" name="score"><br>
            <button type="submit">保存</button>
        </form>
    </div>
</body>
</html>

配置web.xml

<?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">
<!--    修改默认主页-->
    <welcome-file-list>
        <welcome-file>/addStudent.html</welcome-file>
    </welcome-file-list>
    <servlet>
        <servlet-name>studentServlet</servlet-name>
        <servlet-class>com.symc.servlet.StudentServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>studentServlet</servlet-name>
        <url-pattern>/studentServlet</url-pattern>
    </servlet-mapping>
</web-app>

StudentServlet

package com.symc.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Author: 凤文  沈阳医学院2019级医学信息工程 0213
 * @CreateTime: 2021/10/10 21:28
 * @Description:
 */
public class StudentServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取表单数据并保存到文件中
        String name = req.getParameter("name");
        String aClass = req.getParameter("class");
        String score = req.getParameter("score");
//通过流的方式将文件保存到本地的txt文件中
        BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\Documents" +
                "\\Documents\\ProgramProjects\\javaee\\web_case_sys\\web\\get" +
                "\\info.txt",true));
        bw.write(name + "," + aClass + "," + score);
        bw.newLine();
        bw.close();
        //响应客户端浏览器
        PrintWriter pw = resp.getWriter();
        pw.println("Save Success!");
        pw.close();
    }

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

启动服务器,浏览器访问,输入内容,并点击保存
在这里插入图片描述
观察页面响应情况和后台的info.txt文件。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值