1. Servlet 技术

1.1 什么是 Servlet

1、Servlet 是 JavaEE 规范之一。规范就是接口
2、Servlet 就 JavaWeb 三大组件之一。三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器。
3、Servlet 是运行在服务器上的一个 java 小程序,它可以接收客户端发送过来的请求,并响应数据给客户端。

1.2 手动实现 Servlet 程序

步骤:

  1. 编写一个类去实现 Servlet 接口
  2. 实现 service 方法,处理请求,并响应数据
  3. 到 web.xml 中去配置 servlet 程序的访问地址

Servlet 程序的示例代码:
说明:创建一个动态web工程,在创建包,创建一个类实现Servlet接口,实现接口中的方法。在service方法中输出一行进行测试。然后再web.xml配置文件中进行配置,之后通过idea整合的tomact服务器启动,在浏览器中输入地址进行测试,如果在idea控制台中输出了这行测试语句 说明访问成功。

package com.atguigu.servlet;

import javax.servlet.*;
import java.io.IOException;

public class HelloServlet implements Servlet {


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

    }

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

    /**
     * service方法是专门用来处理请求和响应的
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("Hello Servlet 被访问了");
    }

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

    @Override
    public void destroy() {

    }
}

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">


    <!-- servlet标签给Tomcat配置Servlet程序 -->
    <servlet>
        <!--servlet-name标签 Servlet程序起一个别名(一般是类名) -->
        <servlet-name>HelloServlet</servlet-name>
        <!--servlet-class是Servlet程序的全类名-->
        <servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
    </servlet>

    <!--servlet-mapping标签给servlet程序配置访问地址,上下2个名字保持一致-->
    <servlet-mapping>
        <!--servlet-name标签的作用是告诉服务器,我当前配置的地址给哪个Servlet程序使用-->
        <servlet-name>HelloServlet</servlet-name>
        <!--
            url-pattern标签配置访问地址,里面是自定义的访问路径
               / 斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径 ,
                                                   工程路径指的是在idea中进行的配置,一般是修改为当前项目名 详情查看tomact 7.5     <br/>
               /hello 表示地址为:              http://ip:port/工程路径/hello              <br/>
        -->
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>



</web-app>

访问测试:
启动Tomact服务器,在浏览器输入地址:
在这里插入图片描述
可以看出在控制台上输出了HelloServlet类中的service方法的输出语句,说明调用了service方法。
在这里插入图片描述

常见的错误 1:url-pattern 中配置的路径没有以斜杠打头。
在这里插入图片描述

常见错误 2:servlet-name 配置的值不存在:

在这里插入图片描述

常见错误 3:servlet-class 标签的全类名配置错误:

在这里插入图片描述

1.3 Servlet的调用过程

在这里插入图片描述
在这里插入图片描述

1.4 Servlet 的生命周期

1、执行 Servlet 构造器方法
2、执行 init 初始化方法
说明:Servlet 程序默认是第一次访问的时候创建,此时会调用这2个方法。

3、执行 service 方法
说明:第三步,每次访问都会调用。

4、执行 destroy 销毁方法
说明:第四步,在 web 工程停止的时候调用。

测试:
HelloServlet 类:

package com.atguigu.servlet;

import javax.servlet.*;
import java.io.IOException;

public class HelloServlet implements Servlet {
    public HelloServlet() {
        System.out.println("1 构造器方法");
    }

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

        System.out.println("2 init初始化方法");
    }

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

    /**
     * service方法是专门用来处理请求和响应的
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("3 service === Hello Servlet 被访问了");
    }

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

    @Override
    public void destroy() {
        System.out.println("4 . destroy销毁方法");
    }
}

在浏览器输入地址:
在这里插入图片描述
查看控制台输出:
在这里插入图片描述

1.5 GET 和 POST 请求的分发处理

问题:为什么要做请求分发处理???

  • 答:
    执行流程:get请求和post请求都会执行service()方法,不同的请求干的是不同的事情,所以先通过method()方法进行判断是那种请求,之后在根据请求的不同写不同的代码
    缺点:get请求和post请求如果干的事情很多,代码都写在了service方法或造成代码臃肿不好维护。
    解决:可以把get请求和post请求干的事情封装为2个不同的方法doGet()、doPost(),然后再service方法中分别进行调用这2个方法即可。

测试:
步骤1:在web目录下创建一个html文件:a.html,分别使用get请求和post请求。提交的地址写为servlet程序访问的地址。
在这里插入图片描述

<!DOCTYPE html>
<html lang="zh_CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="http://localhost:8080/dongtai-web-servlet/hello" method="get">
        <input type="submit">
    </form>
</body>
</html>

步骤2:在浏览器中先访问这个a.html页面

  • 执行流程:
  1. 首先通过浏览器访问a.html页面,由于这个页面没有写在受保护的WEB-INF目录下,所以可直接访问。格式:https://ip:端口号/项目名(web应用)/资源名。
  2. 之后:通过提交按钮发送get请求访问到servlet程序。
    在这里插入图片描述
    步骤3:点击提交按钮会访问执行servlet程序的service方法。(此时为get请求)
    在这里插入图片描述
    步骤4:在a.html中修改为post再次进行访问测试,可以看出service方法仍然执行。
    在这里插入图片描述
    步骤5:一般get请求和post请求干的事情是不一样的,此时都走service方法无法区分究竟是那个请求,需要先判断下是什么请求,然后分别针对不同的请求做不同的处理,但是这样代码都写在了service方法,会造成代码臃肿不好维护。所以一般会抽取为2个方法get和post, 在service中调用这2个方法即可。
package com.atguigu.servlet;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class HelloServlet implements Servlet {
    public HelloServlet() {
        System.out.println("1 构造器方法");
    }

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

        System.out.println("2 init初始化方法");
    }

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

    /**
     * service方法是专门用来处理请求和响应的
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("3 service === Hello Servlet 被访问了");
        // getMethod()方法是ServletRequest的子类HttpServletRequest提供的,所以需要类型转换
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        // 可以通过getMethod()方法获取请求是那种方式
        String method = httpServletRequest.getMethod();//得到的是大写的 GET POST


        /*
        方式一:这种方式虽然可以进行区分请求类型,但是一般来说get和post请求要做的事情可能很多 代码不止一行,
              如果代码都写在service方法,会造成代码臃肿不好维护。所以一般会抽取为2个方法get和post,
              在service中调用这2个方法即可。
        if ("GET".equals(method)) {
            System.out.println("get请求");
            System.out.println("get请求");
            System.out.println("get请求");
            System.out.println("get请求");
        } else if ("POST".equals(method)) {
            System.out.println("post请求");
            System.out.println("post请求");
            System.out.println("post请求");
            System.out.println("post请求");
        }*/
        //方式二:
        if ("GET".equals(method)) {
            doGet();
        } else if ("POST".equals(method)) {
            doPost();
        }
    }

    /**
     * 做get请求的操作
     */
    public void doGet(){
        System.out.println("get请求");
        System.out.println("get请求");
    }
    /**
     * 做post请求的操作
     */
    public void doPost(){
        System.out.println("post请求");
        System.out.println("post请求");
    }



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

    @Override
    public void destroy() {
        System.out.println("4 . destroy销毁方法");
    }
}

在这里插入图片描述

1.6 通过继承 HttpServlet 实现 Servlet 程序

一般在实际项目开发中,都是使用继承 HttpServlet 类的方式去实现 Servlet 程序。

  1. 编写一个类去继承 HttpServlet 类
  2. 根据业务需要重写 doGet 或 doPost 方法
  3. 到 web.xml 中的配置 Servlet 程序的访问地址

a.xml文件:

<!DOCTYPE html>
<html lang="zh_CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="http://localhost:8080/dongtai-web-servlet/hello02" method="get">
        <input type="submit">
    </form>
</body>
</html>

Servlet 类的代码:

package com.atguigu.servlet;

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

public class HelloServlet02 extends HttpServlet {

    private static final long serialVersionUID = -6975721664720501905L;
    //快捷键重写方法:alt+insert---->Override Methods---->doGet() doPost()
    /**
     * doGet()在get请求的时候调用
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet2 的doGet方法");
    }
    /**
     * doPost()在post请求的时候调用
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet2 的doPost方法");
    }
}

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">

    <servlet>
        <servlet-name>HelloServlet02</servlet-name>
        <servlet-class>com.atguigu.servlet.HelloServlet02</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet02</servlet-name>
        <url-pattern>/hello02</url-pattern>
    </servlet-mapping>



</web-app>

在浏览器中输入地址进行测试:
在这里插入图片描述
点击提交按钮:可以看出是get提交就执行doGet()方法。post请求就执行doPost()方法。

在这里插入图片描述

1.7 使用 IDEA 创建 Servlet 程序

说明:会自动生成doGet() doPost()方法,相比手动创建更简单一些。

注意:

  1. 手动创建的类继承HttpServlet的方式,重写的doGet(),doPost()方法里面方法的参数是:
    (HttpServletRequest req, HttpServletResponse resp)
  2. 使用工具直接创建servlet程序,doGet(),doPost()方法里面方法的参数是:
    (HttpServletRequest request, HttpServletResponse response)
  3. 没有啥区别,只是名字不同而已。

步骤:
菜单:new ->Servlet 程序
在这里插入图片描述

配置 Servlet 的信息:
在这里插入图片描述
HelloServlet03类:

package com.atguigu.servlet;

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

public class HelloServlet03 extends HttpServlet {
    private static final long serialVersionUID = 7204024401528404950L;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("HelloServlet03的doPost方法");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("HelloServlet03的doGet方法");
    }
}

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">


    <servlet>
        <servlet-name>HelloServlet03</servlet-name>
        <servlet-class>com.atguigu.servlet.HelloServlet03</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet03</servlet-name>
        <url-pattern>/hello03</url-pattern>
    </servlet-mapping>

</web-app>

a.html:

<!DOCTYPE html>
<html lang="zh_CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="http://localhost:8080/dongtai-web-servlet/hello03" method="get">
        <input type="submit">
    </form>
</body>
</html>

浏览器访问测试:
在这里插入图片描述
点击提交按钮:可以看出是get提交就执行doGet()方法。post请求就执行doPost()方法。
在这里插入图片描述

1.8 Servlet 类的继承体系

在这里插入图片描述

2. ServletConfig 类(配置信息类)

ServletConfig 类从类名上来看,就知道是 Servlet 程序的配置信息类。

Servlet 程序和ServletConfig 对象都是由 Tomcat 负责创建,我们负责使用。
Servlet 程序默认是第一次访问的时候创建,ServletConfig 是每个 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象。

2.1 ServletConfig 类的三大作用

说明:配置信息类的作用就是获取配置文件web.xml中的内容。

1、可以获取 Servlet 程序的别名 servlet-name 的值
2、获取初始化参数 init-param
3、获取 ServletContext 对象

测试1:ServletConfig可以在初始化的init()方法中使用
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">



    <!-- servlet标签给Tomcat配置Servlet程序 -->
    <servlet>
        <!--servlet-name标签 Servlet程序起一个别名(一般是类名) -->
        <servlet-name>HelloServlet</servlet-name>
        <!--servlet-class是Servlet程序的全类名-->
        <servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>

        <!--init-param是初始化参数-->
        <init-param>
            <!--是参数名-->
            <param-name>username</param-name>
            <!--是参数值-->
            <param-value>root</param-value>
        </init-param>
        <!--init-param是初始化参数-->
        <init-param>
            <!--是参数名-->
            <param-name>url</param-name>
            <!--是参数值-->
            <param-value>jdbc:mysql://localhost:3306/test</param-value>
        </init-param>
    </servlet>

    <!--servlet-mapping标签给servlet程序配置访问地址-->
    <servlet-mapping>
        <!--servlet-name标签的作用是告诉服务器,我当前配置的地址给哪个Servlet程序使用-->
        <servlet-name>HelloServlet</servlet-name>
        <!--
            url-pattern标签配置访问地址                                     <br/>
               / 斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径          <br/>
               /hello 表示地址为:http://ip:port/工程路径/hello              <br/>
        -->
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

</web-app>

HelloServlet 中的代码:
注意:这里使用的是HelloServlet implements Servlet

package com.atguigu.servlet;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class HelloServlet implements Servlet {

    public HelloServlet() {
        System.out.println("1 构造器方法");
    }
    //在init方法中使用
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("2 init初始化方法");

//        1、可以获取Servlet程序的别名servlet-name的值,在web.xml中
        System.out.println("HelloServlet程序的别名是:" + servletConfig.getServletName());//HelloServlet
//        2、获取初始化参数init-param
        System.out.println("初始化参数username的值是;" + servletConfig.getInitParameter("username"));//root
        System.out.println("初始化参数url的值是;" + servletConfig.getInitParameter("url"));//jdbc:mysql://localhost:3306/test
//        3、获取ServletContext对象
        System.out.println(servletConfig.getServletContext());//org.apache.catalina.core.ApplicationContextFacade@6db7b70
    }

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

    /**
     * service方法是专门用来处理请求和响应的
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("3 service === Hello Servlet 被访问了");
        // 类型转换(因为它有getMethod()方法)
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        // 获取请求的方式
        String method = httpServletRequest.getMethod();

        if ("GET".equals(method)) {
            doGet();
        } else if ("POST".equals(method)) {
           doPost();
        }

    }

    /**
     * 做get请求的操作
     */
    public void doGet(){
        System.out.println("get请求");
        System.out.println("get请求");
    }
    /**
     * 做post请求的操作
     */
    public void doPost(){
        System.out.println("post请求");
        System.out.println("post请求");
    }


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

    @Override
    public void destroy() {
        System.out.println("4 . destroy销毁方法");
    }
}

在这里插入图片描述
测试2:除了在init()方法中使用,还可以在别的方法中使用,如doGet()
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">

    <servlet>

        <servlet-name>HelloServlet2</servlet-name>
        <servlet-class>com.atguigu.servlet.HelloServlet2</servlet-class>

        <init-param>
            <param-name>username</param-name>
            <param-value>root2</param-value>
        </init-param>
        <init-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql://localhost:3306/test2</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>HelloServlet2</servlet-name>
        <url-pattern>/hello2</url-pattern>
    </servlet-mapping>
</web-app>

HelloServlet2 中的代码:
注意:这里是HelloServlet2 extends HttpServlet

package com.atguigu.servlet;

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

public class HelloServlet2 extends HttpServlet {

    private static final long serialVersionUID = 253283331602568456L;

    @Override
    public void init(ServletConfig config) throws ServletException {
        /*注意:使用extends HttpServlet方式重写的init方法一定要调用父类的int(config)操作。
        因为:在父类GenericServlet中的init方法会保存这个config对象,在子类重写调用的是子类的
        int()方法 父类的这个config对象保存操作就会丢失,子类没有config在使用是一定会报空指针异常,
        解决:通过super调用父类的init方法即可。*/
        super.init(config);
        System.out.println("重写了init初始化方法,做了一些工作");
    }

    /**
     * doGet()在get请求的时候调用
     * 注意每一个servlet程序都对应着一个自己的ServletConfig,不能在
     * 自己的servlet程序得到别人的servlet信息。
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        System.out.println("HelloServlet2 的doGet方法");
        // 也可以使用.
        ServletConfig servletConfig = getServletConfig();
        System.out.println(servletConfig);

        //        2、获取初始化参数init-param
        System.out.println("初始化参数username的值是;" + servletConfig.getInitParameter("username"));
        System.out.println("初始化参数url的值是;" + servletConfig.getInitParameter("url"));


    }
    /**
     * doPost()在post请求的时候调用
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet2 的doPost方法");
    }
}

访问测试:
在这里插入图片描述
在这里插入图片描述

注意点:

  1. 注意:使用extends HttpServlet方式重写的init方法一定要调用父类的int(config)操作。
    因为:在父类GenericServlet中的init方法会保存这个config对象,在子类重写调用的是子类的nt()方法 父类的这个config对象保存操作就会丢失,子类没有config在使用是一定回报空指针异常。
    解决:通过super调用父类的init方法即可。
    在这里插入图片描述

  2. 注意每一个servlet程序都对应着一个自己的ServletConfig,不能在自己的servlet程序得到别人的servlet信息。

3. ServletContext 类(域对象)

3.1 什么是 ServletContext?

  1. ServletContext 是一个接口,它表示 Servlet 上下文对象
  2. 一个 web 工程,只有一个 ServletContext 对象实例。
  3. ServletContext 对象是一个域对象,就是后面学习的application域对象。
  4. ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁。

什么是域对象?

域对象,是可以像 Map 一样存取数据的对象,叫域对象。

这里的域指的是存取数据的操作范围,整个 web 工程。

Map集合与域对象存取数据对比:

             存数据	          取数据	             删除数据
Map	         put()	          get()	             remove()
域对象	     setAttribute()	  getAttribute()	 removeAttribute();

3.2 ServletContext 类的四个作用

  1. 获取 web.xml 中配置的上下文参数 context-param
  2. 获取当前的工程路径,格式: /工程路径
  3. 获取工程部署后在服务器硬盘上的绝对路径
  4. 像 Map 一样存取数据

测试1:测试前三项功能。
ServletContext 演示代码:

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

public class ContextServlet extends HttpServlet {
    private static final long serialVersionUID = -7502910448759289188L;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//        1、获取web.xml中配置的上下文参数context-param
        ServletContext context = getServletConfig().getServletContext();

        String username = context.getInitParameter("username");
        System.out.println("context-param参数username的值是:" + username);
        System.out.println("context-param参数password的值是:" + context.getInitParameter("password"));
//        2、获取当前的工程路径,格式: /工程路径
        System.out.println( "当前工程路径:" + context.getContextPath() );
//        3、获取工程部署后在服务器硬盘上的绝对路径
        /**
         *  / 斜杠被服务器解析地址为:http://ip:port/工程名/  映射到IDEA代码的web目录<br/>
         */
        System.out.println("工程部署的路径是:" + context.getRealPath("/"));
        System.out.println("工程下css目录的绝对路径是:" + context.getRealPath("/css"));
        System.out.println("工程下imgs目录1.jpg的绝对路径是:" + context.getRealPath("/imgs/1.jpg"));
    }
}

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">

    <!--context-param是上下文参数(它属于整个web工程,也就是说它对整个web工程里面的程序servlet、过滤器等是共用的)-->
    <context-param>
        <param-name>username</param-name>
        <param-value>context</param-value>
    </context-param>
    <!--context-param是上下文参数(它属于整个web工程)-->
    <context-param>
        <param-name>password</param-name>
        <param-value>root</param-value>
    </context-param>

    <servlet>
        <servlet-name>ContextServlet</servlet-name>
        <servlet-class>com.atguigu.servlet.ContextServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ContextServlet</servlet-name>
        <url-pattern>/contextServlet</url-pattern>
    </servlet-mapping>
</web-app>

在这里插入图片描述

在这里插入图片描述

测试2:测试第四项功能,ServletContext 像 Map 一样存取数据:
注意: 数据在保存到域对象之前不能获取,只有保存之后才能获取。
ContextServlet1 代码:

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

public class ContextServlet1 extends HttpServlet {

    private static final long serialVersionUID = 400312190368000524L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取ServletContext对象,也可以直接通过getServletContext()方法获取,这个方法底层封装的还是getServletConfig().getServletContext()
        ServletContext context = getServletContext();
        System.out.println(context);//ContextServlet1和ContextServlet2打印地址相同,说明是一个 web 工程,只有一个 ServletContext 对象实例

        //数据在保存之前不能获取
        System.out.println("保存之前: Context1 获取 key1的值是:"+ context.getAttribute("key1"));

        context.setAttribute("key1", "value1");
        //数据在保存后才能获取值
        System.out.println("Context1 中获取域数据key1的值是:"+ context.getAttribute("key1"));
    }
}

ContextServlet2 代码:

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

public class ContextServlet2 extends HttpServlet {
    private static final long serialVersionUID = 5917018552732354379L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext context = getServletContext();
        System.out.println(context);
        //只要不重启整个web工程,那么在整个web项目下,都可以获取域对象在保存之后的值。
        System.out.println("Context2 中获取域数据key1的值是:"+ context.getAttribute("key1"));
    }
}

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">

   


    <servlet>
        <servlet-name>ContextServlet1</servlet-name>
        <servlet-class>com.atguigu.servlet.ContextServlet1</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ContextServlet1</servlet-name>
        <url-pattern>/contextServlet1</url-pattern>
    </servlet-mapping>



    <servlet>
        <servlet-name>ContextServlet2</servlet-name>
        <servlet-class>com.atguigu.servlet.ContextServlet2</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ContextServlet2</servlet-name>
        <url-pattern>/contextServlet2</url-pattern>
    </servlet-mapping>



</web-app>

在浏览器中分别输入2个路径进行测试:
在这里插入图片描述
在这里插入图片描述

4. HTTP 协议

4.1 什么是 HTTP 协议

什么是协议?

  • 协议是指双方,或多方,相互约定好,大家都需要遵守的规则,叫协议。

什么是HTTP 协议?

  • 所谓 HTTP 协议(超文本传输协议),就是指,客户端和服务器之间通信时,发送的数据,需要遵守的规则,叫 HTTP 协议。
    在这里插入图片描述
  • HTTP 协议中的数据又叫报文

HTTP 协议的特点:

  1. 基于TCP协议:面向连接,安全(三次握手和四次挥手)
  2. 基于请----响应模型的:一次请求对应一次响应
  3. HTTP协议是无状态的协议:对于事务处理没有记忆能力。每次请求----响应都是独立的,后一次请求是不会记录前一次请求数据的。
    • 缺点:多次请求间不能共享数据。
    • 优点:速度快

4.2 请求的 HTTP 协议格式

  • 请求:客户端给服务器发送数据叫请求
  • 响应:服务器给客户端回传数据叫响应
  • 请求种类:GET 请求,和 POST 请求两种

4.2.1 GET 请求

1、请求行

  1. 请求的方式 GET
  2. 请求的资源路径[+?+请求参数]
  3. 请求的协议的版本号 HTTP/1.1

2、请求头
key : value 组成 不同的键值对,表示不同的含义。

注意:GET 请求没有请求体,POST请求只有携带参数才有请求体。
在这里插入图片描述

4.2.2 POST 请求

1、请求行

  1. 请求的方式 POST
  2. 请求的资源路径[+?+请求参数]
  3. 请求的协议的版本号 HTTP/1.1

2、请求头

  1. key : value 不同的请求头,有不同的含义.
  2. 请求头和请求体之间有 空行

3、请求体 ===>>> 就是发送给服务器的数据

注意

  • post提交和get提交的请求参数格式相同,只不过是请求参数提交的位置不同。
  • GET 请求没有请求体,POST请求只有携带参数才有请求体。
    在这里插入图片描述

4.2.3 常用请求头的说明

在这里插入图片描述

  • Accept: 表示客户端可以接收的数据类型

  • Accpet-Languege: 表示客户端可以接收的语言类型

  • User-Agent: 表示客户端浏览器的信息

  • Host: 表示请求时的服务器 ip 和端口号

4.2.4 哪些是 GET 请求,哪些是 POST 请求

GET 请求有哪些:

  1. form 标签 method=get
  2. a 标签
  3. link 标签引入 css
  4. Script 标签引入 js 文件
  5. img 标签引入图片
  6. iframe 引入 html 页面
  7. 在浏览器地址栏中输入地址后敲回车

POST 请求有哪些:

  1. form 标签 method=post

总结:在没写Ajax之前,只有当使用表单(form),并且在表单上明确的通过method指定提交方式为POST时,请求方式才是POST提交,其它方式都是GET提交。

4.2.5 GET 请求和POST 请求的区别

在这里插入图片描述

  1. get请求一般是获取数据,post请求一般的提交数据。
  2. post请求安全、get请求不安全。
  3. 本质区别是传参的方式不一样:
    • get请求在地址栏后面以?的方式传参,多个参数之间用&分隔。
    • post请求是在body以表单的方式传参。

4.3 响应的 HTTP 协议格式

4.3.1 响应行、响应头、响应体

1、响应行

  1. 响应的协议和版本号
  2. 响应状态码
  3. 响应状态描述符

2、响应头

  • key : value 不同的响应头,有其不同含义。
  • 响应头和响应体之间有 空行

在这里插入图片描述
3、响应体 ---->>> 就是回传给客户端的数据

在这里插入图片描述

4.3.2 常用的响应码说明

  • 常见的响应状态码分类
    在这里插入图片描述
  • 常见的响应状态码
    状态码大全:https://cloud.tencent.com/developer/chapter/13553在这里插入图片描述
    200 表示请求成功
    302 表示请求重定向(明天讲)
    404 表示请求服务器已经收到了,但是你要的数据不存在(请求地址错误)
    500 表示服务器已经收到请求,但是服务器内部错误(代码错误)

4.4 HTTP-协议解析

  • 浏览器和服务器之间通信需要遵守http协议,解析请求数据和响应数据分为2个部分:客户端、服务端
    • 客户端:各大厂商提供了内置的解析http协议的程序,我们不需要操作。
    • 服务端:作为服务端开发工程师,我们要做的是在服务器端通过java程序来接收客户端浏览器发起的请求并获取请求数据,然后参照http协议的请求数据格式对请求数据进行解析,然后还需要参照http协议的响应数据格式给浏览器在响应对应的数据。
      • 具体做法:可以使用原生的方式TCP网络编程,通过socket关键字来编写java程序进行接收和响应数据,但是这种方式太过繁琐。因为Web项目开发需要解析http协议,而http协议又是标准的、统一固定的,所以这部分解析http协议的代码也是非常通用的,所以市面上很多公司封装了这些通用的代码到一个软件中,这个软件程序就是web服务器
      • 常见的Web服务器
        在这里插入图片描述

4.5 MIME 类型说明

MIME 是 HTTP 协议中数据类型。
MIME 的英文全称是"Multipurpose Internet Mail Extensions" 多功能 Internet 邮件扩充服务。MIME 类型的格式是“大类型/小类型”,并与某一种文件的扩展名相对应。

常见的 MIME 类型:
在这里插入图片描述

4.6 谷歌浏览器如何查看 HTTP 协议:

在这里插入图片描述

4.7 火狐浏览器如何查看 HTTP 协议:

在这里插入图片描述

5. HttpServletRequest 类

5.1 HttpServletRequest 类有什么作用。

每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息解析好封装到 Request 对象中(即:发送一次请求,就对应一个request对象)。然后传递到 service 方法(doGet 和 doPost)中给我们使用。我们可以通过 HttpServletRequest 对象,获取到所有请求的信息。

注意:

  1. 手动创建的类继承HttpServlet的方式,重写的doGet(),doPost()方法里面方法的参数是:
    (HttpServletRequest req, HttpServletResponse resp)
  2. 使用工具直接创建servlet程序,doGet(),doPost()方法里面方法的参数是:
    (HttpServletRequest request, HttpServletResponse response)
  3. 没有啥区别,只是名字不同而已。

5.2 HttpServletRequest 类的常用方法

i.getRequestURI()	获取请求的资源路径
ii.getRequestURL()	获取请求的统一资源定位符(绝对路径)
iii.getRemoteHost()	获取客户端的 ip 地址
iv.getHeader()	获取请求头
v.getParameter()	获取请求的参数
vi.getParameterValues()	获取请求的参数(多个值的时候使用)
vii.getMethod()	获取请求的方式GETPOST
viii.setAttribute(key, value);	设置域数据
ix.getAttribute(key);	获取域数据
x.getRequestDispatcher()	获取请求转发对象

测试常用API 示例代码:
说明:首先创建一个动态web工程,建议修改tomac启动的实例化名和项目名保持一致,在修改工程访问路径一般也和创建的模块名保持一致。

在这里插入图片描述
在这里插入图片描述
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">


    <servlet>
        <servlet-name>RequestAPIServlet</servlet-name>
        <servlet-class>com.atguigu.servlet.RequestAPIServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>RequestAPIServlet</servlet-name>
        <url-pattern>/requestAPIServlet</url-pattern>
    </servlet-mapping>

</web-app>

Servlet程序:RequestAPIServlet

package com.atguigu.servlet;

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

public class RequestAPIServlet extends HttpServlet {

    private static final long serialVersionUID = 6929316627289085482L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //        i.getRequestURI()					获取请求的资源路径
        System.out.println("URI => " + req.getRequestURI());
//        ii.getRequestURL()					获取请求的统一资源定位符(绝对路径)
        System.out.println("URL => " + req.getRequestURL());
//        iii.getRemoteHost()				获取客户端的ip地址
        /**
         * 在IDEA中,使用localhost访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1<br/>
         * 在IDEA中,使用127.0.0.1访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1<br/>
         * 在IDEA中,使用 真实ip 访问时,得到的客户端 ip 地址是 ===>>> 真实的客户端 ip 地址<br/>
         */
        System.out.println("客户端 ip地址 => " + req.getRemoteHost());
//        iv.getHeader()						获取请求头
        System.out.println("请求头User-Agent ==>> " + req.getHeader("User-Agent"));
//        vii.getMethod()					获取请求的方式GET或POST
        System.out.println( "请求的方式 ==>> " + req.getMethod() );
    }
}

在浏览器中输入地址访问tomact服务器中的servlet程序(前提:tomact先启动)。
在这里插入图片描述
在这里插入图片描述

5.3 如何获取请求参数

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">

    <servlet>
        <servlet-name>ParameterServlet</servlet-name>
        <servlet-class>com.atguigu.servlet.ParameterServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ParameterServlet</servlet-name>
        <url-pattern>/parameterServlet</url-pattern>
    </servlet-mapping>

</web-app>

表单fom.html:分别使用get,post请求,在web目录下

<!DOCTYPE html>
<html lang="zh_CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://localhost:8080/dongtai-web-servlet2/parameterServlet" method="post">
    用户名:<input type="text" name="username"><br/>
    密码:<input type="password" name="password"><br/>
    兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++
    <input type="checkbox" name="hobby" value="java">Java
    <input type="checkbox" name="hobby" value="js">JavaScript<br/>
    <input type="submit">
</form>
</body>
</html>

Java 代码:

package com.atguigu.servlet;

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.Arrays;

public class ParameterServlet extends HttpServlet {

    private static final long serialVersionUID = 2944615058694247321L;

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

        // 获取请求参数,值为表单name属性的值 (这是通过form表单提交参数,获取里面的请求参数的方式)
        String username = req.getParameter("username");
        //getParameter()获取一个参数值,如果相同的name值有多个,这个方法只能获取第一个的value值。
        String password = req.getParameter("password");
        //getParameterValues()获取一个参数数组
        String[] hobby = req.getParameterValues("hobby");

        System.out.println("用户名:" + username);
        System.out.println("密码:" + password);
        //数组直接输出的是地址,使用toString()方法,或者转化为集合在输出 因为集合中的数据String是系统提供的类,会自动重写toString()方法。
        System.out.println("兴趣爱好:" + Arrays.toString(hobby));//方式一
        //System.out.println("兴趣爱好:" + Arrays.asList(hobby));//方式二
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-------------doPost------------");
        // 获取请求参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobby = req.getParameterValues("hobby");

        System.out.println("用户名:" + username);
        System.out.println("密码:" + password);
        System.out.println("兴趣爱好:" + Arrays.asList(hobby));
    }
}

分别使用get和post请求,在浏览器中输入地址,添加数据后点击提交按钮,可以看出不论是get还是post都可以获取到请求参数。
在这里插入图片描述
在这里插入图片描述

5.4 Get 请求的中文乱码解决:

如果请求是GET提交,并且tomcat是8.0及以后的版本,GET提交的中文参数,在获取时不会出现乱码问题!(8.0以后的tomcat包括8.0在获取GET提交的中文参数时,已经处理中文乱码问题。)

如果请求是GET提交,并且tomcat是7.0及以前的版本,GET提交的中文参数,在获取时会出现乱码问题!

解决:在[tomcat安装目录]/ conf/server.xml文件的(修改端口的)Connector标签上,添加一个 URIEncoding=“utf-8” 属性,如下:

<Connector port="80" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"
           URIEncoding="utf-8" />

5.5 POST 请求的中文乱码解决

问题:get请求的参数值(接收用户输入文本框的值)是中文也不会乱码,post请求的参数值(接收用户输入文本框的值)是中文会发生乱码

解决:设置请求体的字符集为UTF-8,从而解决post请求的中文乱码问题

注意:要在获取请求参数之前调用才有效
在这里插入图片描述

package com.atguigu.servlet;

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.Arrays;

public class ParameterServlet extends HttpServlet {

    private static final long serialVersionUID = 2944615058694247321L;

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

        // 获取请求参数,值为表单name属性的值 (这是通过form表单提交参数,获取里面的请求参数的方式)
        String username = req.getParameter("username");
        //getParameter()获取一个参数值,如果相同的name值有多个,这个方法只能获取第一个的value值。
        String password = req.getParameter("password");
        //getParameterValues()获取一个参数数组
        String[] hobby = req.getParameterValues("hobby");

        System.out.println("用户名:" + username);
        System.out.println("密码:" + password);
        //数组直接输出的是地址,使用toString()方法,或者转化为集合在输出 因为集合中的数据String是系统提供的类,会自动重写toString()方法。
        System.out.println("兴趣爱好:" + Arrays.toString(hobby));//方式一
        //System.out.println("兴趣爱好:" + Arrays.asList(hobby));//方式二
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-------------doPost------------");
        //问题:get请求的参数值(接收用户输入文本框的值)是中文也不会乱码,post请求的参数值(接收用户输入文本框的值)是中文会发生乱码
        // 解决:设置请求体的字符集为UTF-8,从而解决post请求的中文乱码问题
        // 注意:要在获取请求参数之前调用才有效
        req.setCharacterEncoding("UTF-8");
        // 获取请求参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobby = req.getParameterValues("hobby");

        System.out.println("用户名:" + username);
        System.out.println("密码:" + password);
        System.out.println("兴趣爱好:" + Arrays.asList(hobby));
    }
}

5.6 请求的转发(作为域对象携带数据)

什么是请求的转发?
请求转发是指,服务器收到请求后,从一个资源跳转到另一个资源的操作叫请求转发(属于服务器内部资源的跳转)。
在这里插入图片描述
测试1:
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">
    <servlet>
        <servlet-name>Servlet1</servlet-name>
        <servlet-class>com.atguigu.servlet.Servlet1</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Servlet1</servlet-name>
        <url-pattern>/servlet1</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>Servlet2</servlet-name>
        <servlet-class>com.atguigu.servlet.Servlet2</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Servlet2</servlet-name>
        <url-pattern>/servlet2</url-pattern>
    </servlet-mapping>

</web-app>

Servlet1 代码:

package com.atguigu.servlet;

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

public class Servlet1 extends HttpServlet {
    private static final long serialVersionUID = -6204548247948921305L;

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

        // 获取请求的参数(办事的材料)查看
        String username = req.getParameter("username");
        System.out.println("在Servlet1(柜台1)中查看参数(材料):" + username);

        // 给材料 盖一个章,并传递到Servlet2(柜台 2)去查看 把数据存入到域对象
        req.setAttribute("key1","柜台1的章");//此对象也有域方法,类似于ServletContex域对象

        // 问路:Servlet2(柜台 2)怎么走
        /**
         * 请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到IDEA代码的web目录<br/>
         * 路径:为xml文件中配置的servlet2的访问路径
         */
       RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
     
        // 走向Sevlet2(柜台 2) Dispatcher: 调度员
        requestDispatcher.forward(req,resp);

    }
}


Servlet2 代码:

package com.atguigu.servlet;

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

public class Servlet2 extends HttpServlet {
    private static final long serialVersionUID = -302871879227334765L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取请求的参数(办事的材料)查看
        String username = req.getParameter("username");
        System.out.println("在Servlet2(柜台2)中查看参数(材料):" + username);

        // 查看 柜台1 是否有盖章 取出域对象存入的数据
        Object key1 = req.getAttribute("key1");//要和存的key保持一致
        System.out.println("柜台1是否有章:" + key1);

        // 处理自己的业务
        System.out.println("Servlet2 处理自己的业务 ");
    }
}


在浏览器中输入地址访问servlet1的地址,并添加请求参数进行测试:
在这里插入图片描述
可以看到在servlet2中拿到了servlet1存入域对象的数据,说明转发成功。
在这里插入图片描述
测试2:可以转发到WEB-INF目录下
把form.html放到WEB-INF目录下:

<!DOCTYPE html>
<html lang="zh_CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="http://localhost:8080/dongtai-web-servlet2/parameterServlet" method="post">
    用户名:<input type="text" name="username"><br/>
    密码:<input type="password" name="password"><br/>
    兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++
    <input type="checkbox" name="hobby" value="java">Java
    <input type="checkbox" name="hobby" value="js">JavaScript<br/>
    <input type="submit">
</form>
</body>
</html>

修改转发路径:
在这里插入图片描述

package com.atguigu.servlet;

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

public class Servlet1 extends HttpServlet {
    private static final long serialVersionUID = -6204548247948921305L;

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

        // 获取请求的参数(办事的材料)查看
        String username = req.getParameter("username");
        System.out.println("在Servlet1(柜台1)中查看参数(材料):" + username);

        // 给材料 盖一个章,并传递到Servlet2(柜台 2)去查看
        req.setAttribute("key1","柜台1的章");//此对象也有域方法,类似于ServletContex域对象

        // 问路:Servlet2(柜台 2)怎么走
        /**
         * 请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到IDEA代码的web目录<br/>
         * 路径:为xml文件中配置的servlet2的访问路径
         */

        RequestDispatcher requestDispatcher = req.getRequestDispatcher("/WEB-INF/form.html");

        requestDispatcher.forward(req,resp);//执行转发




    }
}

在浏览器中直接输入地址不能访问WEB-INF目录下的文件。
在这里插入图片描述
但是通过请求转发可以进行访问,在浏览器输入地址访问servlet1的程序,在servlet1中通过请求转发访问WEB-INF目录下的form.html文件。
在这里插入图片描述
测试3:不可以转发到工程以外的资源。
修改servlet1的访问路径:
在这里插入图片描述

package com.atguigu.servlet;

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

public class Servlet1 extends HttpServlet {
    private static final long serialVersionUID = -6204548247948921305L;

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

        // 获取请求的参数(办事的材料)查看
        String username = req.getParameter("username");
        System.out.println("在Servlet1(柜台1)中查看参数(材料):" + username);

        // 给材料 盖一个章,并传递到Servlet2(柜台 2)去查看
        req.setAttribute("key1","柜台1的章");//此对象也有域方法,类似于ServletContex域对象

        // 问路:Servlet2(柜台 2)怎么走
        /**
         * 请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到IDEA代码的web目录<br/>
         * 路径:为xml文件中配置的servlet2的访问路径
         */

        RequestDispatcher requestDispatcher = req.getRequestDispatcher("http://www.baidu.com");

        requestDispatcher.forward(req,resp);//执行转发




    }
}

在浏览器中输入servlet1的访问地址进行测试:可以看出不可以转发到工程以外的资源。
在这里插入图片描述

5.7 base 标签的作用

解释:相对路径跳转原理:
浏览器地址:http://localhost:8080/07_servlet/a/b/c.html
跳转回去的路径是:../../index.html
参考后的得到地址过程解释:http://localhost:8080/07_servlet/a/b/c.html ---》../../index.html
../代表当前文件下所在目录的父目录,即c.html处在父目录a下,去掉../相当于把a目录下的目录文件去掉变为http://localhost:8080/07_servlet/a/ ---》../index.html。在次去掉../相当于把07_servlet下的目录去掉,变为http://localhost:8080/07_servlet/index.html
总结:去掉一层..相当于去掉一层目录。

在这里插入图片描述
注意:请求转发的浏览器地址不会发生变化指的是,访问servlet的过程到另一个servlet的浏览器地址不会改变。(地址浏览器–访问–> A --转发–> B,地址栏地址始终指向A的地址。)

而这里指的是一个通过servlet请求转发访问,一个是直接访问web/a/b/c.html目录下的资源,浏览器路径当然会发生变化。

测试:
1.在web下创建index.html文件

<!DOCTYPE html>
<html lang="zh_CN">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
这是Web下的index.html <br/>
<!--省略了./  用的是相对路径,解释:
        2个页面结构为:web/index.html    web/a/b/c.html 都在同一个web下,所以./就表示相对于web目录,
        而项目启动自动访问到index.html页面的地址,http://localhost:8080/07_servlet/index.html
        web目录就是项目名,所以相对于web目录就是相对于项目名,即./省略了http://localhost:8080/07_servlet     
        -->
<a href="a/b/c.html">a/b/c.html</a><br/>
<a href="http://localhost:8080/dongtai-web-servlet2/forwardC">请求转发:a/b/c.html</a><br/>
</body>
</html>

2.在web目录下创建a目录,在a目录下创建b目录,在b目录下创建c.html文件。

<!DOCTYPE html>
<html lang="zh_CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--base标签设置页面相对路径工作时参照的地址
            href 属性就是参数的地址值
     注意:base标签中最后一个资源名可以省略,即c.html可以省略,但是/不能省,有/才代表是个目录,没有
     斜杠就是个文件了。
    -->
    <base href="http://localhost:8080/dongtai-web-servlet2/a/b/c.html">
</head>
<body>
这是a下的b下的c.html页面<br/>
<a href="../../index.html">跳回首页</a><br/>
</body>
</html>

3.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">

    <servlet>
        <servlet-name>ForwardC</servlet-name>
        <servlet-class>com.atguigu.servlet.ForwardC</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ForwardC</servlet-name>
        <url-pattern>/forwardC</url-pattern>
    </servlet-mapping>

</web-app>

4.servlet程序:

package com.atguigu.servlet;

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

public class ForwardC extends HttpServlet {
    private static final long serialVersionUID = -5960088181218849289L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("经过了ForwardC程序");
        req.getRequestDispatcher("/a/b/c.html").forward(req, resp);
    }
}

说明:
问题:启动tomact服务器,会自动访问web工程下的index.html页面,在index页面分别使用超链接直接跳转到c.html页面 和 使用超链接访问servlet程序 在通过servlet程序请求转发跳转到c.html页面,此时都可以调转成功。但是反过来在c.html页面使用超链接跳转到index.html,发现只有是通过超链接跳转到c.html页面的能够反过来跳转,而使用servlet程序请求转发跳转到的c.html不能反过来跳转。

原因:使用servlet程序进行求求转发跳转时,浏览器的访问路径发生了改变,而在跳转回去时相对路径是参照当前浏览器的路径而进行跳转的,此时浏览器地址发生了改变,也就不能跳转成功。

解决:使用base标签设置当前页面所有相对路径工作时,参考那个路径进行跳转。

在c.html中设置返回index.html页面时,相对路径参考的浏览器地址:
在这里插入图片描述

此时:从index.html页面跳转到c.html页面,在从c.html页面回跳到index.html页面,2中方式都能成功跳转和返回。
在这里插入图片描述
在这里插入图片描述

5.8 回顾:Web中的相对路径和绝对路径

在 javaWeb 中,路径分为相对路径和绝对路径两种:

相对路径是:

.	      表示当前目录
..	      表示上一级目录
资源名	  表示当前目录/资源名

绝对路径:

http://ip:port/工程路径/资源路径

在实际开发中,路径都使用绝对路径,而不简单的使用相对路径

1、绝对路径
2、base+相对

5.9 web中路径以/斜杠开头的不同意义

在 web 中 / 斜杠 是一种绝对路径。

/ 斜杠如果被浏览器解析,得到的地址是:http://ip:port/
如: <a href="/">斜杠</a>

/ 斜杠 如果被服务器解析,得到的地址是:http://ip:port/工程路径
如:
1、<url-pattern>/servlet1</url-pattern> web.xml配置的servlet路径时讲的
2、servletContext.getRealPath(“/”); 域对象时讲的
3、request.getRequestDispatcher(“/”); 请求转发时讲的

特殊情况,请求重定向: response.sendRediect(“/”); 虽然是写在服务器上,但是他不直接解析,而是把斜杠发送给浏览器解析。得到 http://ip:port/

6. HttpServletResponse 类

6.1 HttpServletResponse 类的作用

HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个 Response 对象传递给 Servlet 程序去使用。

HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse 对象来进行设置。

6.2 两个输出流的说明。

说明:服务器响应数据返回给客户端,是通过流的方式返回的。

字节流	getOutputStream();	  常用于下载(传递二进制数据)
字符流	getWriter();	      常用于回传字符串(常用)

两个流同时只能使用一个。
使用了字节流,就不能再使用字符流,反之亦然,否则就会报错。

测试:
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">
    <servlet>
        <servlet-name>ResponseIOServlet</servlet-name>
        <servlet-class>com.atguigu.servlet.ResponseIOServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ResponseIOServlet</servlet-name>
        <url-pattern>/responseIOServlet</url-pattern>
    </servlet-mapping>

</web-app>

servlet程序:

package com.atguigu.servlet;

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

public class ResponseIOServlet extends HttpServlet {
    private static final long serialVersionUID = -1915614522680378148L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //两个流同时只能使用一个。
        //使用了字节流,就不能再使用字符流,反之亦然,否则就会报错。
        response.getWriter();//使用字节流
        response.getOutputStream();//使用字符流
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

注意:因为是在浏览器输入地址是get请求,调用的是doGet()方法,所以测试代码要写在doGet()方法中测试。

启动tomact服务器,在浏览器输入访问sevlet的地址进行测试,会报异常。
在这里插入图片描述
原因:
在这里插入图片描述

6.3 如何往客户端回传数据

要求 : 往客户端回传 字符串 数据。

测试:
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">

    <servlet>
        <servlet-name>ResponseIOServlet</servlet-name>
        <servlet-class>com.atguigu.servlet.ResponseIOServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ResponseIOServlet</servlet-name>
        <url-pattern>/responseIOServlet</url-pattern>
    </servlet-mapping>


</web-app>

servlet程序:

package com.atguigu.servlet;

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.io.PrintWriter;

public class ResponseIOServlet extends HttpServlet {
    private static final long serialVersionUID = -1915614522680378148L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //要求 : 往客户端回传 字符串 数据。
        PrintWriter wirter = response.getWriter();
        wirter.write("response  text");

    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

启动tomact,在浏览器输入地址进行测试:
在这里插入图片描述

6.4 响应的乱码解决

说明:响应的数据是中文时会产生乱码,需要设置字符集,有2种方式。

package com.atguigu.servlet;

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.io.PrintWriter;

public class ResponseIOServlet extends HttpServlet {
    private static final long serialVersionUID = -1915614522680378148L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //查看响应的默认字符集
        System.out.println( response.getCharacterEncoding() );//默认ISO-8859-1

        //方式一:分开写
        // 设置服务器字符集为UTF-8
        //response.setCharacterEncoding("UTF-8");
        // 通过响应头,设置浏览器也使用UTF-8字符集  响应的数据类型    文本/html    编码
        //response.setHeader("Content-Type", "text/html; charset=UTF-8");

        //方式二(推荐使用):一步到位  
        // 它会同时设置服务器和客户端都使用UTF-8字符集,还设置了响应头
        // 此方法一定要在获取流对象之前调用才有效
        response.setContentType("text/html; charset=UTF-8");


        //要求 : 往客户端回传 字符串 数据。
        PrintWriter wirter = response.getWriter();
        wirter.write("国哥很帅!!!!");

    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

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">

    <servlet>
        <servlet-name>ResponseIOServlet</servlet-name>
        <servlet-class>com.atguigu.servlet.ResponseIOServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ResponseIOServlet</servlet-name>
        <url-pattern>/responseIOServlet</url-pattern>
    </servlet-mapping>


</web-app>

在浏览器输入访问servlet程序的地址,可以看到返回给浏览器页面的中文可以正确显示。
在这里插入图片描述

6.5 请求重定向

请求重定向,是指客户端给服务器发请求,然后服务器告诉客户端说:我给你一些地址,你去新地址访问,叫请求重定向(因为之前的地址可能已经被废弃)。
在这里插入图片描述
测试:
Response1:

package com.atguigu.servlet;

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

public class Response1 extends HttpServlet {

    private static final long serialVersionUID = -6342912739252936213L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("曾到此一游 Response1 ");
        //测试1:重定向在域对象中存入数据是否能够共享。不共享说明重定向不能携带数据。
        req.setAttribute("key1", "value1");
        //方式一:
        // 设置响应状态码302 ,表示重定向,(已搬迁)
        //resp.setStatus(302);        
        // 设置响应头说明 新的地址在哪里
        //resp.setHeader("Location", "http://localhost:8080/dongtai-web-servlet2/response2");  
        //方式二(推荐使用)  302是固定的在源码中设置好了,你不需要在设置
        resp.sendRedirect("http://localhost:8080/dongtai-web-servlet2/response2");
              
        //测试2:重定向不能转发到WEB-INF目录下的资源
        //原因:浏览器不能直接访问WEB-INF写的资源,而重定向的2次发送请求都是通过浏览器发送的,所以重定向不能转发到WEB-INF目录下的资源
         //方式一:Location:位置,地址
        //resp.setHeader("Location", "http://localhost:8080/dongtai-web-servlet2/WEB-INF/form.html");    
            
        //测试3:可以访问当前程序以外的资源
        //resp.setHeader("Location", "http://www.baidu.com");
        //方式二(推荐使用)  302是固定的在源码中设置好了,你不需要在设置
        //resp.sendRedirect("http://www.baidu.com");



    }
}


Response2:

package com.atguigu.servlet;

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

public class Response2 extends HttpServlet {

    private static final long serialVersionUID = 5018722793226197068L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取域对象中的数据为null,说明重定向中的域对象数据不共享 ,也就不能携带数据。
        //原因:tomact每次收到请求就会把请求中的数据解析好封装成一个request对象中,重定向是发送了
        //     一个新的请求 地址栏发生了变化,也就是一个新的request对象,这个新的request对象哪会有原来request对象携带的数据。
        System.out.println(req.getAttribute("key1"));//null
        resp.getWriter().write("response2's result!");
    }
}

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">

    <servlet>
        <servlet-name>Response1</servlet-name>
        <servlet-class>com.atguigu.servlet.Response1</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Response1</servlet-name>
        <url-pattern>/response1</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>Response2</servlet-name>
        <servlet-class>com.atguigu.servlet.Response2</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Response2</servlet-name>
        <url-pattern>/response2</url-pattern>
    </servlet-mapping>


</web-app>

在这里插入图片描述
启动tomact服务器,在浏览器输入访问Response1的servlet地址,可以看到重定向到百度成功。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐