Servlet

1、Servlet

1.1 Servlet概述

        Servlet是SUN公司提供的一套规范,名称就叫Servlet规范,它也是JavaEE规范之一。我们可以像学习Java基础一样,通过API来学习Servlet。这里需要注意的是,在我们之前JDK的API中是没有Servlet规范的相关内容,需要使用JavaEE的API。

        目前在Oracle官网中的最新版本是[JavaEE8](https://www.oracle.com/technetwork/java/javaee/documentation/ee8-release-notes-3894362.html),该网址中介绍了JavaEE8的一些新特性。当然,我们可以通过访问[官方API](https://javaee.github.io/javaee-spec/javadocs/),学习和查阅里面的内容。

        打开官方API网址,在左上部分找到javax.servlet包,在左下部分找到Servlet,如下图显示:

        通过阅读API,我们得到如下信息:

                第一:Servlet是一个运行在web服务端的java小程序

                第二:它可以用于接收和响应客户端的请求

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

                第四:每次请求都会执行service方法

                第五:Servlet还支持配置

具体请看下图:

1.2 Servlet入门

1.2.1 Servlet编码步骤

第一步:前期准备-创建JavaWeb工程

第二步:编写一个普通类继承GenericServlet并重写service方法

package com.itheima.servlet;
/*
    Servlet快速入门1
 */

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-10-30
 * Time:15:02
 */
public class ServletDemo01 extends GenericServlet {

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service 已经运行了。。。。。");
    }
}

第三步:在web.xml配置Servlet

<?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_3_1.xsd"
         version="3.1">
    <!--servlet快速入门1的配置-->
    <!--声明-->
    <servlet>
        <servlet-name>servletDemo01</servlet-name>
        <servlet-class>com.itheima.servlet.ServletDemo01</servlet-class>
    </servlet>
    <!--映射servlet-->
    <servlet-mapping>
        <servlet-name>servletDemo01</servlet-name> <!--这个名称要和我们在声明中的名称保持一致-->
        <url-pattern>/servletDemo01</url-pattern>
    </servlet-mapping>
</web-app>

测试

第一步:在Tomcat中部署项目

        做法:点击idea上的run,然后在点击Edit Configuration 

第二步:在浏览器访问Servlet

        做法:在网址上输入路径名

1.2.2 Servlet执行过程分析

        我们通过浏览器发送请求,请求首先到达Tomcat服务器,由服务器解析请求URL,然后在部署的应用列表中找到我们的应用。接下来,在我们的应用中找应用里的web.xml配置文件,在web.xml中找到FirstServlet的配置,找到后执行service方法,最后由FirstServlet响应客户浏览器。整个过程如下图所示:

        一句话总结执行过程:

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

1.2.3 Servlet类视图

        在Servlet的API介绍中,它提出了我们除了继承GenericServlet外还可以继承HttpServlet,通过查阅servlet的类视图,我们看到GenericServlet还有一个子类HttpServlet。同时,在service方法中还有参数ServletRequest和ServletResponse,它们的关系如下图所示:

1.2.4 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 )。

2)HttpServlet的使用细节

第一步:在入门案例的工程中创建一个Servlet继承HttpServlet

        注意:不要重写任何方法,如下图所示:

 

第二步:部署项目并测试访问

        当我们在地址栏输入ServletDemo2的访问URL时,出现了访问错误,状态码是405。提示信息是:方法不允许。

第三步:分析原因

        得出HttpServlet的使用结论:

        我们继承了HttpServlet,需要重写里面的doGet和doPost方法来接收get方式和post方式的请求。

        为了实现代码的可重用性,我们只需要在doGet或者doPost方法中一个里面提供具体功能即可,而另外的那个方法只需要调用提供了功能的方法。

1.3 Servlet使用细节

1.3.1 Servlet的生命周期

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

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

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

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

        通过分析Servlet的生命周期我们发现,它的实例化和初始化只会在请求第一次到达Servlet时执行,而销毁只会在Tomcat服务器停止时执行,由此我们得出一个结论,Servlet对象只会创建一次,销毁一次。所以,Servlet对象只有一个实例。如果一个对象实例在应用中是唯一的存在,那么我们就说它是单实例的,即运用了单例模式。

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

        启动两个浏览器,输入不同的参数,访问之后发现输出的结果都是一样,所以出现线程安全问题

        通过上面的测试我们发现,在Servlet中定义了类成员之后,多个浏览器都会共享类成员的数据。其实每一个浏览器端发送请求,就代表是一个线程,那么多个浏览器就是多个线程,所以测试的结果说明了多个线程会共享Servlet类成员中的数据,其中任何一个线程修改了数据,都会影响其他线程。因此,我们可以认为Servlet它不是线程安全的。

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

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

1.3.3 Servlet的注意事项

1)映射Servlet的细节

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

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

/**
 * 演示Servlet的映射方式
 * @author 黑马程序员
 * @Company http://www.itheima.com
 */
public class ServletDemo5 extends HttpServlet {

    /**
     * doGet方法输出一句话
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("ServletDemo5接收到了请求");
    }

    /**
     * 调用doGet方法
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

第一种:指名道姓的方式

​               此种方式,只有和映射配置一模一样时,Servlet才会接收和响应来自客户端的请求。

​               例如:映射为:/servletDemo5

​                           访问URL:http://localhost:8585/servlet_demo/servletDemo5

第二种:/开头+通配符的方式

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

        例如:映射为:/servlet/*

        访问URL:http://localhost:8585/servlet/itheima

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

        这两个URL都可以。因为用的*,表示/servlet/后面的内容是什么都可以。

第三种:通配符+固定格式结尾

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

        例如:映射为:*.do

        访问URL:http://localhost:8585/servlet/itcast.do

http://localhost:8585/itheima.do

        这两个URL都可以实现。因为都是以.do作为结尾,而前面用*号通配符配置的映射,所以无须关心。

        通过测试我们发现,Servlet支持多种配置方式,但是由此也引出了一个问题,当有两个及以上的Servlet映射都符合请求URL时,由谁来响应呢? 

        注意:HTTP协议的特征是一请求一响应的规则。那么有一个请求,必然有且只有一个响应。所以,我们接下来明确一下,多种映射规则的优先级。

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

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

2)多路径映射Servlet

        Servlet的多种路径配置的支持,其实就是给一个Servlet配置多个访问映射,从而可以根据不同请求URL实现不同的功能。

        首先,创建一个Servlet:

package com.itheima.servlet;

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

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-10-31
 * Time:15:16
 */
public class ServletDemo05 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        int money = 1000;
        //1、获取当前请求的uri
        String path = req.getRequestURI();
        path = path.substring(path.lastIndexOf("/") + 1);
        //2、判断是vip请求还是vvip请求
        if("vip".equals(path)){
            //如果是vip那么就打9折
            System.out.println("原价为:" + money + "," + "打折后的价格为:" + (money * 0.9));
       }else if("vvip".equals(path)){
            //如果是vvip那么就打5折
            System.out.println("原价为:" + money + "," + "打折后的价格为:" + (money * 0.5));
        }else {
            System.out.println("价格为:" + money);
        }
    }

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

        接下来,在web.xml配置Servlet:

<!--配置ServletDemo05-->
    <servlet>
        <servlet-name>servletDemo05</servlet-name>
        <servlet-class>com.itheima.servlet.ServletDemo05</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletDemo05</servlet-name>
        <url-pattern>/servlet/*</url-pattern>
    </servlet-mapping>

         最后,启动服务测试运行结果:

3)启动时创建Servlet

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

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

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

        通过上面的描述,相信同学们都能分析得出何时采用第一种方式,何时采用第二种方式。就是当需要在应用加载就要完成一些工作时,就需要选择第一种方式。当有很多Servlet的使用时机并不确定是,就选择第二种方式。

        在web.xml中是支持对Servlet的创建时机进行配置的,配置的方式如下:

  1. 修改Servlet创建时机。在<servlet>标签中,添加<load-on-startup>标签
  2. 正整数代表服务器加载时创建,值越小,优先级越高。负整数或不写代表第一次访问时创建。
<!--配置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>

4)默认Servlet

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

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

1.4 Servlet关系总图

 

2、ServletConfig

2.1 ServletConfig概述

2.1.1 基本概念

        它是Servlet的配置参数对象,在Servlet规范中,允许为每个Servlet都提供一些初始化配置。所以,每个Servlet都一个自己的ServletConfig。

        作用:是在Servlet初始化期间,把一些配置信息传递给Servlet。

2.1.2 生命周期

        由于它是在初始化阶段读取了web.xml中为Servlet准备的初始化配置,并把配置信息传递给Servlet,所以生命周期与Servlet相同。

        这里需要注意的是,如果Servlet配置了<load-on-startup>1</load-on-startup>,那么ServletConfig也会在应用加载时创建。

2.2 ServletConfig的使用

2.2.1 如何获取

        首先,我们要清楚的认识到,它可以为每个Servlet都提供初始化参数,所以肯定可以在每个Servlet中都配置。

2.2.2 如何配置

        使用<servlet>标签中的<init-param>标签来配置。

        Servlet的初始化参数都是配置在Servlet的声明部分的。

        并且每个Servlet都支持有多个初始化参数,并且初始化参数都是以键值对的形式存在的。接下来,我们看配置示例:

2.2.3 常用方法 

测试案例:

package com.itheima.config;

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;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-10-31
 * Time:16:15
 */
public class ServletConfigDemo01 extends HttpServlet {
    //定义Servlet配置对象ServletConfig
    private ServletConfig servletConfig;

    /**
     * 在初始化时为ServletConfig赋值
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.servletConfig = config;
    }

    /**
     * doGet方法输出一句话
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.输出ServletConfig
        System.out.println(servletConfig);
        //2.获取Servlet的名称
        String servletName= servletConfig.getServletName();
        System.out.println(servletName);
        //3.获取字符集编码
        String encoding = servletConfig.getInitParameter("encoding");
        System.out.println(encoding);
        //4.获取所有初始化参数名称的枚举
        Enumeration<String> names = servletConfig.getInitParameterNames();
        //遍历names
        while(names.hasMoreElements()){
            //取出每个name
            String name = names.nextElement();
            //根据key获取value
            String value = servletConfig.getInitParameter(name);
            System.out.println("name:"+name+",value:"+value);
        }
        //5.获取ServletContext对象
        ServletContext servletContext = servletConfig.getServletContext();
        System.out.println(servletContext);
    }

    /**
     * 调用doGet方法
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @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_3_1.xsd"
         version="3.1">

    <!--配置ServletConfigDemo01-->
    <servlet>
        <servlet-name>servletConfigDemo01</servlet-name>
        <servlet-class>com.itheima.config.ServletConfigDemo01</servlet-class>
        <!--配置初始化参数-->
        <init-param>
            <!--用于获取初始化参数的key-->
            <param-name>encoding</param-name>
            <!--初始化参数的值-->
            <param-value>UTF-8</param-value>
        </init-param>
        <!--每个初始化参数都需要用到init-param标签,即每一个init-param标签中,只能有一个键值对-->
        <init-param>
            <param-name>servletInfo</param-name>
            <param-value>This is Demo8</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletConfigDemo01</servlet-name>
        <url-pattern>/servletConfigDemo01</url-pattern>
    </servlet-mapping>
</web-app>

3、ServletContext

3.1 ServletContext概述

3.1.1 基本介绍

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

        作用:可以配置和获得应用的全局初始化参数,可以实现让应用中所有Servlet间的数据共享。

3.1.2 生命周期

        出生——活着——死亡

        出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象。(Servlet和ServletContext都是单例的)

        活着:只要应用一直提供服务,该对象就一直存在。

        死亡:应用被卸载(或者服务器挂了),该对象消亡。

3.1.3 域对象概念

        域对象的概念,它指的是对象有作用域,即有作用范围。

        域对象的作用:域对象可以实现数据共享。不同作用范围的域对象,共享数据的能力不一样。

        在Servlet规范中,一共有4个域对象。今天我们讲解的ServletContext就是其中一个。它也是我们接触的第一个域对象。它是web应用中最大的作用域,叫application域。每个应用只有一个application域。它可以实现整个应用间的数据共享功能。

3.2 ServletContext的使用

3.2.1 ServletContext配置

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

        配置的方式,需要<web-app>标签中使用<context-param>来配置初始化参数。具体代码如下:

<!--配置应用初始化参数-->
<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>

3.2.2 ServletContext常用方法

public class ServletContextDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取ServletContext对象
        ServletContext context = getServletContext();

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

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

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

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

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

        //获取a.txt文件的绝对路径
        String a = context.getRealPath("/WEB-INF/classes/a.txt");
        System.out.println(a);


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

        //移除域对象中username的数据
        //context.removeAttribute("username");
    }

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

共享数据常用的方法: 

//设置共享数据
context.setAttribute("username","zhangsan");

//删除共享数据
context.removeAttribute("username");
//获取共享数据
Object value = servletContext.getAttribute("username");
System.out.println(value);

4 注解开发Servlet

4.1 Servlet3.0规范

        首先,我们要先跟同学们明确一件事情,我们在《Tomcat和HTTP协议》课程中已经介绍了,我们使用的是Tomcat9,JavaEE规范要求是8,对应的Servlet规范规范应该是JavaEE8包含的4.x版本。

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

        接下来,同学还有可能疑惑的地方就是,我们课程中明明使用的是Servlet3.1版本的规范,但是却总听老师提Servlet3.0规范,这两个到底有怎样的联系呢?

        现在就给同学们解惑,在大概十多年前,那会还是Servlet2.5的版本的天下,它最明显的特征就是Servlet的配置要求配在web.xml中,我们今天课程中在第4章节《注解开发Servlet》之前,全都是基于Servlet2.5规范编写的。从2007年开始到2009年底,在这个时间段,软件开发开始逐步的演变,基于注解的配置理念开始逐渐出现,大量注解配置思想开始用于各种框架的设计中,例如:Spring3.0版本的Java Based Configuration,JPA规范,apache旗下的struts2和mybatis的注解配置开发等等。

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

4.2 注解开发入门案例

4.2.1 自动注解配置

1)配置步骤

第一步:创建JavaWeb工程,并移除web.xml

 

 第二步:编写Servlet

package com.itheima.servlet;

import com.sun.org.glassfish.gmbal.NameValue;

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;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-10-31
 * Time:20:59
 */
@WebServlet("/servletDemo01")
public class ServletDemo01 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);
    }
}

第三步:使用注解配置Servlet

 第四步:测试

 

2)注解详解

/**
 * 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 "";
}

5、Servlet应用案例-学生管理系统

5.1 案例介绍

5.1.1 案例需求

        今天,我们将会去解决下面这个问题:保存学生。也就是让数据真正的动起来,本质就是通过html发送一个请求,把表单中填写的数据带到服务器端。因为每个使用者在表单填写的内容不一样,所有最终存起来的也就不一样了。

5.1.2 技术选型

        这是一个全新的案例,而不是在SE阶段的案例上进行改造。所以我们用项目的方式来去约束这个案例。

        任何一个项目,在立项之初都会有技术选型,也就是定义使用的技术集,这里面包含很多。例如:表现层技术,持久层技术,数据库技术等等。

        我们今天只针对表现层进行编码,所以就先来定义表现层技术。表现层技术的选型就是Servlet+HTML的组合。

        由HTML中编写表单,Servlet中定义接收请求的方法,最终把表单数据输出到控制台即可。我们Servlet的配置方式仍然选择基于web.xml的配置方式。

5.2 执行步骤

第一步:在web目录下编写addStudent.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>保存学生信息</title>
</head>
<body>
    <form action="/stu/studentServlet" method="get" autocomplete="off">
        学生姓名:<input type="text" name="username"> <br>
        学生年龄:<input type="number" name="userage"> <br>
        学生成绩:<input type="number" name="userscore"> <br>
        <button type="submit">保存</button>
    </form>
</body>
</html>

第二步:在src目录下编写servler的功能类

package com.itheima.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;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-01
 * Time:10:10
 */
public class StudentServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取表单数据并保存到文件中
        String username = req.getParameter("username");
        String userage = req.getParameter("userage");
        String userscore = req.getParameter("userscore");

        //采用字符输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\java\\stu.txt",true));
        bw.write(username + "," + userage + "," + userscore);
        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);
    }
}

第三步:在web.xml文件中,首先修改默认主页。然后对servler进行配置,然后部署项目

<?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>
        <servlet-name>studentServlet</servlet-name>
        <servlet-class>com.itheima.servlet.StudentServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>studentServlet</servlet-name>
        <url-pattern>/studentServlet</url-pattern>
    </servlet-mapping>
</web-app>

        最后一步:在浏览器上进行测试

6、请求对象

6.1 请求对象概述

6.1.1 关于请求

        请求,就是使用者希望从服务器端索取一些资源,向服务器发出询问。在B/S架构中,就是客户浏览器向服务器发出询问。在我们的JavaEE工程中,客户浏览器发出询问,要遵循HTTP协议所规定的。

        请求对象,就是在JavaEE工程中,用于发送请求的对象。我们常用的对象就是ServletRequest和HttpServletRequest,它们的区别就是是否和HTTP协议有关。                

6.2 常用方法介绍

6.2.1 获取各种路径

 6.2.2 获取请求头信息

 6.2.3 获取请求参数信息

6.2.4 获取请求参数并封装对象

1、手动封装对象

package com.itheima.servlet;
import com.itheima.bean.Student;
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;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-01
 * Time:16:42
 */
@WebServlet("/servletDemo04")
public class ServletDemo04 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1、根据名称获取数据
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String hobby = req.getParameter("hobby");

        //将得到的数据进行封装
        Student stu = new Student(username,password,hobby);

        //对得到的数据进行打印
        System.out.println(stu);
    }

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

2、反射封装对象

package com.itheima.servlet;

import com.itheima.bean.Student;

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.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-01
 * Time:16:42
 */
@WebServlet("/servletDemo05")
public class ServletDemo05 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1、获取所有的数据
        Map<String, String[]> map = req.getParameterMap();

        //2、封装学生对象
        Student stu = new Student();

        //2.1 遍历集合
        for(String name : map.keySet()){
            String[] value = map.get(name);

            try {
                //2.2 获取Student对象的属性描述器
                PropertyDescriptor pd = new PropertyDescriptor(name,stu.getClass());
                //2.3 获取对应的setXxx方法
                Method writeMethod = pd.getWriteMethod();
                //2.4 执行方法
                if(value.length > 1){
                    writeMethod.invoke(stu,(Object) value);
                }else {
                    writeMethod.invoke(stu,value);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //3、输出对象
        System.out.println(stu);
    }

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

3、工具类封装对象

第一步:要将jar包添加进Tomcat服务器中 

package com.itheima.servlet;

import com.itheima.bean.Student;
import org.apache.commons.beanutils.BeanUtils;

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.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-01
 * Time:16:42
 */
@WebServlet("/servletDemo06")
public class ServletDemo06 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1、获取所有的数据
        Map<String, String[]> map = req.getParameterMap();

        //2、封装学生对象
        Student stu = new Student();
        try {
            BeanUtils.populate(stu,map);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //3、输出对象
        System.out.println(stu);
    }

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

6.2.5 用流的形式读取请求信息

package com.itheima.servlet;

import com.itheima.bean.Student;
import org.apache.commons.beanutils.BeanUtils;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Map;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-01
 * Time:16:42
 */
@WebServlet("/servletDemo07")
public class ServletDemo07 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*//字符流(必须是post方式)
        BufferedReader reader = req.getReader();
        String line;
        while ((line = reader.readLine()) != null){
            System.out.println(line);
        }
        //reader.close();*/

        //字节流
        ServletInputStream inputStream = req.getInputStream();
        byte[] arr = new byte[1024];
        int len;
        while ((len = inputStream.read(arr)) != -1){
            System.out.println(new String(arr,0,len));
        }
        //inputStream.close();
    }

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

 6.2.6 中文乱码问题

  • GET方式

                没有乱码问题,在Tomcat 8版本后已经解决

  • POST方式

                有乱码问题。可以通过setCharacterEncoding()方法来解决

6.2.7 请求域

 6.2.8 请求转发

  • 请求转发:客户端的一次请求到达后,发现需要借助其他servlet来实现功能
  • 特点:

                浏览器地址栏不变

                域对象中的数据不丢失

                负责转发的Servlet转发前后的响应正文会丢失

                由转发的目的地来响应客户端

package com.itheima.servlet;

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

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-01
 * Time:16:42
 */
@WebServlet("/servletDemo08")
public class ServletDemo08 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //建设一个共享数据
        req.setAttribute("encoding","jbk");

        //获取调度对象
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servletDemo09");

        //通过调度对象实现转发
        requestDispatcher.forward(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
package com.itheima.servlet;

import javax.servlet.RequestDispatcher;
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;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-01
 * Time:16:42
 */
@WebServlet("/servletDemo09")
public class ServletDemo09 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //得到共享数据
        Object encoding = req.getAttribute("encoding");
        System.out.println(encoding);

        System.out.println("servletDemo09执行了........");
    }

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

 6.2.9 请求包含

  • 请求包含:可以合并其他Servlet中的功能一起响应给客户端
  • 特点:

                浏览器地址栏不变

                域对象中的数据不丢失

                被包含的Servlet响应头会丢失

package com.itheima.servlet;

import javax.servlet.RequestDispatcher;
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;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-01
 * Time:16:42
 */
@WebServlet("/servletDemo10")
public class ServletDemo10 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("servletDemo10执行了........");

        //获取请求调度对象
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servletDemo11");

        //调用实现包含的方法
        requestDispatcher.include(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
package com.itheima.servlet;

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;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-01
 * Time:16:42
 */
@WebServlet("/servletDemo11")
public class ServletDemo11 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("servletDemo11执行了........");
    }

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

7、响应对象

7.1 响应对象概述

7.1.1 关于响应

        响应,它表示了服务器端收到请求,同时也已经处理完成,把处理的结果告知用户。简单来说,指的就是服务器把请求的处理结果告知客户端。在B/S架构中,响应就是把结果带回浏览器。

        响应对象,就是用于在JavaWeb工程中实现上述功能的对象。

7.1.2 常用响应对象

        响应对象也是Servlet规范中定义的,它包括了协议无关的和协议相关的。

        协议无关的对象标准是:ServletResponse接口

        协议相关的对象标准是:HttpServletResponse接口

类结构图如下:

        我们课程中涉及的响应对象都是和HTTP协议相关的。即使用的是HttpServletResponse接口的实现类。

        这里有些同学可能会产生疑问,我们在使用Servlet时,需要定义一个类,然后实现Servlet接口(或者继承它的实现类)。现在我们想要实现响应功能,要不要定义一个类,然后实现HttpServletResponse接口呢?

        此问题的答案是否定的,我们无需这么做。我们只需要在自己写的Servlet中直接使用即可,因为这个对象的实现类是由Tomcat提供的,无须我们自定义。同时它还会帮我们把对象创建出来并传入doGet和doPost方法中。

7.2 常用方法介绍

        在HttpServletResponse接口中提供了很多方法,接下来我们通过API文档,来了解一下这些方法。

7.2.1 常用状态码

7.3 响应对象的使用示例

7.3.1 响应-字节流输出中文问题

package com.itheima.servlet;

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

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-02
 * Time:19:53
 */
@WebServlet("/servletDemo01")
public class ServletDemo01 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
         
        //1、获取字节输出流对象
        ServletOutputStream os = resp.getOutputStream();

        //2、定义一个消息
        String str = "你好,世界";

        //3、通过字节流对象输出
        os.write(str.getBytes());
    }

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

7.3.2 响应-字符流输出中文问题

package com.itheima.servlet;

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

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-02
 * Time:19:53
 */
@WebServlet("/servletDemo02")
public class ServletDemo02 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");

        //1、获取字符流输出对象
        PrintWriter pw = resp.getWriter();

        //2、准备一个消息
        String str = "呆逼";

        //3、通过字符流输出
        pw.write(str);

        //pw.close();
    }

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

7.3.3 响应图片

1、创建字节输入流对象,关联读取的图片路径。

2、通过响应对象获取字节输出流对象。

3、循环读取和写出图片。

package com.itheima.servlet;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-02
 * Time:19:53
 */
@WebServlet("/servletDemo03")
public class ServletDemo03 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获得图片的绝对路径
        String realpath = getServletContext().getRealPath("/img/hm.png");

        //1、创建字节输入流对象,关联图片路径
        FileInputStream is = new FileInputStream(realpath);

        BufferedInputStream bis = new BufferedInputStream(is);

        //2、通过响应对象获取字节输出流对象
        ServletOutputStream os = resp.getOutputStream();

        //3、循环读写
        byte[] arr = new byte[1024];
        int len;
        while ((len = bis.read(arr)) != -1){
            os.write(arr,0,len);
        }

        //4、释放资源
        bis.close();
        is.close();
    }

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

7.3.4 设置缓存

package com.itheima.servlet;

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;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-02
 * Time:19:53
 */
/*
    设置缓存时间
 */
@WebServlet("/servletDemo05")
public class ServletDemo05 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String str = "设置缓存时间";

        resp.setDateHeader("Expires",System.currentTimeMillis() + 1 * 60 * 60 * 1000);

        resp.setContentType("text/html;charset=UTF-8");

        resp.getOutputStream().print(str);
    }

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

 7.3.5 定时刷新

        参数:name  =  Refresh         value =  2;URL= 要跳转的连接

 7.3.6 请求重定向

package com.itheima.servlet;

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;


/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-02
 * Time:19:53
 */
@WebServlet("/servletDemo06")
public class ServletDemo06 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("servletDemo06已经执行了........");

        //设置请求域共享数据
        req.setAttribute("username","呆逼");

        //请求重定向
        //resp.sendRedirect(req.getContextPath() + "/servletDemo07");

        //重定向到其他的服务器上
        resp.sendRedirect("https://www.baidu.com");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
package com.itheima.servlet;

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;

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-02
 * Time:19:53
 */
@WebServlet("/servletDemo07")
public class ServletDemo07 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("servletDemo07已经执行了........");

        //获取共享数据
        Object username = req.getAttribute("username");
        System.out.println(username);


    }

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

7.3.7 文件下载

1、创建字节输入流,关联读取的文件

2、设置响应消息头支持的类型

3、设置响应消息头以下载方式打开资源

4、通过响应对象获取字节输出流对象

5、循环读写

6、释放资源

package com.itheima.servlet;

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

/**
 * Created with InteIIiJ IDEA.
 * Description:
 * User:qal
 * Date:2021-11-02
 * Time:19:53
 */

/**
 * 文件下载
 */

@WebServlet("/servletDemo08")
public class ServletDemo08 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取绝对路径
        String realPath = getServletContext().getRealPath("/img/hm.png");

        //1、创建字节输入流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));

        //2、设置响应头支持的类型
        /*
            Content-Type: 消息头名称
            application/octet-stream: 消息头参数 应用的类型为字节流
         */

        resp.setHeader("Content-Type","application/octet-stream");

        //3、设置响应头以下载方式打开附件
        /*
            Content-Disposition: 消息头名称  处理的形式
            attachment;filename=hm.png: 消息头参数 附件形式进行处理 指定下载文件的名称
         */
        resp.setHeader("Content-Disposition","attachment;filename=hm.png");

        //4、获取字节输出流对象
        ServletOutputStream os = resp.getOutputStream();

        //5、循环读写
        byte[] arr = new byte[1024];
        int len;
        while ((len = bis.read(arr)) != -1){
            os.write(arr,0,len);
        }

        //6、释放资源
        bis.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值