Servlet

发布时间 2023-08-31 16:27:37作者: 一往而深,

什么是Servlet


第一个Servlet程序

关于Servlet可以参考JavaEE文档

  • Servlet是一个接口,我们只用实现这个接口就可以 了
    关于为什么Servlet标红,因为Servlet是JavaEE里面的内容,我们点击红色的内容按照引导,下载JavaEE即可
  • 实现步骤

    我们需要去配置一下Servlet程序的访问地址,否则我们的服务器不知道Servlet程序

  • Servlet程序(实现Servlet接口并重写Service方法)
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;
    }

    @Override
    //service方法是专门用来处理请求和相应的
    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() {

    }
}


  • 给servlet程序配置地址(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程序的类名)-->
        <servlet-name>HelloServlet</servlet-name>
        <!--servlet-class标签是servlet程序的全类名-->
        <servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--servlet-mapping标签给servlet程序配置访问地址-->
<servlet-mapping>
    <!--servlet-name标签作用是告诉服务器配置的地址给哪个servlet程序使用(一般和上面的servlet-name相同)-->

    <servlet-name>HelloServlet</servlet-name>
    <!--url-pattern标签配置访问地址 <br/>
    /斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径    <br/>

    /hello 表示地址为http://ip:port/工程路径/hello

    -->
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
  • 1.我们打开服务器将自动访问index.jsp文件
  • 此时我们的程序将会自动访问servlet程序中重写的service方法

Servlet程序常见错误

  • 1.建议servlet类名要和其地址有对应关系(其地址可以自己配置)
  • 错误1.url-pattern中配置的路径不以斜杠开头
  • 错误2:servlet-name值不存在
  • 错误3:servlet-class标签的全类名出错
    这个在IDEA中可以通过输入类名自动补全来避免这个错误

url地址如何定位到servlet程序去访问


Servlet生命周期方法

  • 验证这个过程
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;
    }

    @Override
    //service方法是专门用来处理请求和相应的
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("3.service方法");
    }

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

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

  • 当我连续再次方法,发现只有service方法被方法

    我们发现我们连续多次方法,我们构造方法只会执行一次,说明我们所访问的实例是同一个,说明我们的servlet对象是单例的

  • 停止工程时会执行destroy方法销毁实例

servlet请求的分发处理

我们的service方法专门用来处理请求的,但是我们将html的时候知道有get和post两种请求

  • 我们写一个html文件用来验证请求的处理
  • 在web目录下创建
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--需要提交的表单:提交给servlet程序处理-->
    <form action="http://localhost:8080/tomcatTest2/hello" method="get">
      <input type="submit">
    </form>
</body>
</html>

  • 当我们点击提交将会访问servlet程序

  • 当我们将提交方式改成post并且再次提交

  • 修改后我们需要更新类和资源

  • 查看源代码确认已经修改

  • 点击提交后发现访问的还是同样的service方法

但是get和post请求干的事情是不一样的,我们需要实现这一点

我们可以通过获取现在请求的方式,来判断请求的类型,进而实现不同的请求干不同的事情

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

    @Override
    //service方法是专门用来处理请求和相应的
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //类型转换(因为HttpServletRequest有getMethod方法)
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
       String method = httpServletRequest.getMethod();//获取请求方式
        if("GET".equals(method)){
            System.out.println("GET请求");
        }else if("POST".equals(method)){
            System.out.println("POST请求");
        }

    }

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

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

但是如果我们的代码很多,都写在if else语句中会十分臃肿,所以我们可以将处理get和post请求的语句集成为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;
    }

    @Override
    //service方法是专门用来处理请求和相应的
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //类型转换(因为HttpServletRequest有getMethod方法)
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
       String method = httpServletRequest.getMethod();//获取请求方式
        if("GET".equals(method)){
           doGet();
        }else if("POST".equals(method)){
            doPost();
        }

    }
   public void doGet(){
       System.out.println("处理get请求");
       System.out.println("处理get请求");
   }
   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销毁方法");
    }
}


通过继承HttpServlet实现Servlet程序

在实际的开发中我们一般很少使用实现Servlet接口的方式去实现Servlet程序

我们通过继承HttpServlet类,并且重写HttpServlet类中的doGet和doPost方法,来实现

  • 通过继承HttpServlet的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;
//继承HttpServlet类,并重写doGet和doPost方法
public class HelloServlet2 extends HttpServlet {
    /*
       *doGet()在get请求的时候调用
       *
       
     */
       @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet2的doGet方法");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet2的doPost方法");
    }
}

  • 此时我们还需要在web.xml文件中标注新的servlet程序的访问地址
  • 此时我们的web.xml文件中有2个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_4_0.xsd"
         version="4.0">
    <!--servlet标签给Tomcat配置Servlet程序-->
    <servlet>
        <!--servlet-name标签给servlet程序起一个别名(一般是servlet程序的类名)-->
        <servlet-name>HelloServlet</servlet-name>
        <!--servlet-class标签是servlet程序的全类名(输入类名可以自动生成)-->
        <servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--servlet-mapping标签给servlet程序配置访问地址-->
<servlet-mapping>
    <!--servlet-name标签作用是告诉服务器配置的地址给哪个servlet程序使用(一般和上面的servlet-name相同)-->

    <servlet-name>HelloServlet</servlet-name>
    <!--url-pattern标签配置访问地址 <br/>
    /斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径    <br/>

    /hello 表示地址为http://ip:port/工程路径/hello

    -->
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

    <servlet>
        <servlet-name>HelloServlet2</servlet-name><!--servlet程序的别名-->
        <servlet-class>com.atguigu.servlet.HelloServlet2</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet2</servlet-name>
        <url-pattern>/hello2</url-pattern>
    </servlet-mapping>


</web-app>
  • 我们将a.html文件中表单提交的跳转地址改成新的servlet程序的地址就可以使用了

和前面的方式相比:我们的新的方式不需要判断提交的方式是get还是post。我们的程序可以自动根据提交的方式自动调用doGet或者是doPost方法

通过IDEA创建Servlet程序

在实际的开发中确实是用继承自HttpServlet类的方式实现Servlet程序,但是我们可以借助IDEA自动生成

  • 将会自动生成

  • 1.servlet程序的配置地址信息

  • 2.继承自httpServlet的servlet程序

后面将a.html文件的提交地址改成新的servlet程序的地址即可使用了

整个Servlet类的继承体系


小总结
我们每次访问servlet程序的时候将会自动调用servlet的service方法,我们的HelloServlet程序继承自HttpServlet,所以将会调用HttpService的servicCe方法,service方法中将会调用doGet和doPost,而这2个方法我们在HelloServlet类中进行了重写,所以将会调用重写后的方法

ServletConfig类的使用介绍

init-parm是参数,在配置的时候可以配置多组

  • 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_4_0.xsd"
         version="4.0">
    <!--servlet标签给Tomcat配置Servlet程序-->
    <servlet>
        <!--servlet-name标签给servlet程序起一个别名(一般是servlet程序的类名)-->
        <servlet-name>HelloServlet</servlet-name>
        <!--servlet-class标签是servlet程序的全类名(输入类名可以自动生成)-->
        <servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
        <init-param>
            <!--参数值(是一个键值对)-->
            <param-name>username</param-name><!--参数名-->
            <param-value>root</param-value><!--参数值-->
        </init-param>
    </servlet>
    <!--servlet-mapping标签给servlet程序配置访问地址-->
<servlet-mapping>
    <!--servlet-name标签作用是告诉服务器配置的地址给哪个servlet程序使用(一般和上面的servlet-name相同)-->

    <servlet-name>HelloServlet</servlet-name>
    <!--url-pattern标签配置访问地址 <br/>
    /斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径    <br/>

    /hello 表示地址为http://ip:port/工程路径/hello

    -->
    <url-pattern>/hello</url-pattern>
</servlet-mapping>




    <servlet>
        <servlet-name>HelloServlet3</servlet-name>
        <servlet-class>com.atguigu.servlet.HelloServlet3</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet3</servlet-name>
        <url-pattern>/hello3</url-pattern>
    </servlet-mapping>






</web-app>
  • servlet程序
package com.atguigu.servlet;

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
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初始化方法");
        //1.可以获取Servlet程序别名servlet-name
        System.out.println("HelloServlet程序的别名servlet-name:"+servletConfig.getServletName());
        //2. 可以获取初始化参数init-param
        System.out.println("初始参数username的值是:"+servletConfig.getInitParameter("username"));
        //3.可以获取ServletContext对象
        System.out.println("ServletContext对象:"+servletConfig.getServletContext());
    }

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

    @Override
    //service方法是专门用来处理请求和相应的
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //类型转换(因为HttpServletRequest有getMethod方法)
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
       String method = httpServletRequest.getMethod();//获取请求方式
        if("GET".equals(method)){
           doGet();
        }else if("POST".equals(method)){
            doPost();
        }


    }
   public void doGet(){

       System.out.println("处理get请求");
       System.out.println("处理get请求");
   }
   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.在之前的web.xml配置中,我们没有配置init-parm(初始化参数),在演示的时候必须配置
    我们的ServletConfig类是专门记录Servlet程序配置信息的类,在Servlet程序被创建的时候,将会随之创建一个ServletConfig对象用来记录Servlet程序的配置。当在执行init方法的时候,将会读取web.xml文件的信息,此时的ServletConfig对象也会被初始化。
    所以在我们输出一些ServletConfig对象的信息的时候,其实他已经被初始化了

ServletConfig类的补充说明

每一个ServletConfig对象是对应他自己的Servlet程序的

  • 1.在servlet程序中其他地方也可以使用servletConfig对象
  • 2.每一个ServletConfig对象对应自己的servlet程序,只能获取自己servlet程序的配置
  • 包含3个servlet程序配置信息的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程序的类名)-->
        <servlet-name>HelloServlet</servlet-name>
        <!--servlet-class标签是servlet程序的全类名(输入类名可以自动生成)-->
        <servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
        <init-param>
            <!--参数值(是一个键值对)-->
            <param-name>username</param-name><!--参数名-->
            <param-value>root</param-value><!--参数值-->
        </init-param>
    </servlet>
    <!--servlet-mapping标签给servlet程序配置访问地址-->
<servlet-mapping>
    <!--servlet-name标签作用是告诉服务器配置的地址给哪个servlet程序使用(一般和上面的servlet-name相同)-->

    <servlet-name>HelloServlet</servlet-name>
    <!--url-pattern标签配置访问地址 <br/>
    /斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径    <br/>

    /hello 表示地址为http://ip:port/工程路径/hello

    -->
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

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

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


    <servlet>
        <servlet-name>HelloServlet3</servlet-name>
        <servlet-class>com.atguigu.servlet.HelloServlet3</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet3</servlet-name>
        <url-pattern>/hello3</url-pattern>
    </servlet-mapping>

</web-app>
package com.atguigu.servlet;

import javax.servlet.GenericServlet;
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;
//继承HttpServlet类,并重写doGet和doPost方法
public class HelloServlet2 extends HttpServlet {
    /*
       *doGet()在get请求的时候调用
       *

     */
       @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet2的doGet方法");
           final ServletConfig servletConfig = super.getServletConfig();//获取ServletConfig对象
           System.out.println(servletConfig);//org.apache.catalina.core.StandardWrapperFacade@7bb1a9aa
           //2. 可以获取初始化参数init-param(我们用这个类的servletConfig对象获取别的servlet程序的配置信息)
           System.out.println("初始参数username的值是:"+servletConfig.getInitParameter("username"));//null

       }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet2的doPost方法");
    }
}

  • 最后当然是得不到别的servlet程序的配置信息
  • 3.当我们重写了init(ServletConfit confit)(有参Init方法)将会出现空指针异常
    -servlet程序
package com.atguigu.servlet;

import javax.servlet.GenericServlet;
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;
//继承HttpServlet类,并重写doGet和doPost方法
public class HelloServlet2 extends HttpServlet {
    //重写init方法
    @Override
    public void init(ServletConfig config) throws ServletException {
    
        System.out.println("重写了init初始化方法,做了一些工作");
    }
    // *doGet()在get请求的时候调用

       @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet2的doGet方法");
           final ServletConfig servletConfig = super.getServletConfig();//获取ServletConfig对象
           System.out.println(servletConfig);//org.apache.catalina.core.StandardWrapperFacade@7bb1a9aa
           //2. 可以获取初始化参数init-param(我们用这个类的servletConfig对象获取别的servlet程序的配置信息)
           System.out.println("初始参数username的值是:"+servletConfig.getInitParameter("username"));//null

       }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet2的doPost方法");
    }
}

  • 为什么会这样呢
  • 我们打开该方法的父类GenericServlet类



    此时就可以解析这个异常了。当我们重写了inint方法,此时在创建servlet程序的时候调用的就是重写后的Init方法,此时并没有将ServletConfig引用保存下来,所以我们通过getInitParameter方法得到到对象就为null

ServletContext对象的介绍

ServletContext对象的作用演示


init-param只能是由ServletConfig对象获取,context-param只能由ServletContext对象获取

  • 我们看到获取init-param和context-param使用的方法都是一样的,只是调用的对象不同,但是也不能由ServletContext对象获取init-param(两者不能串用)
package com.atguigu.servlet;

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

public class ContextServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取 web.xml 中配置的上下文参数 context-param
         ServletContext context = super.getServletConfig().getServletContext();//获取ServletContext对象
        System.out.println("context-param参数username的值是:"    +context.getInitParameter("username"));
        System.out.println("context-param参数password的值是:"    +context.getInitParameter("password"));
        //2、获取当前的工程路径,格式: /工程路径
        System.out.println(context.getContextPath());//  /tomcatTest
        ///3、获取工程部署后在服务器硬盘上的绝对路径
        /*
        *  /斜杠被服务器解析为:http://ip:port/工程名/
        * /斜杠映射到idea代码的web目录
        *
         */
        System.out.println("工程部署的路径是"+context.getRealPath("/"));
        System.out.println("css目录的绝对路是"+context.getRealPath("/css"));
    }

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

    }
}



ServletContext像Map一样存取数据

**web.xml里面ServletContext程序的地址里面/没有写,为什么会报错

解释:在我们启动服务器的时候,将会创建servlet程序的实例对象(一般是servlet接口的实现子类),在这个过程中会读取web.xml文件中servlet程序的配置信息。所以当在读取配置信息的时候servlet程序的访问地址不正确,将会报错

  • 默认已经配置好了访问地址
package com.atguigu.servlet;

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

public class ContextServlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取ServletContext对象(底层也是先获取ServletConfig对象,然后获取ServletContext对象)
      ServletContext context = super.getServletContext();
        System.out.println("保存之前:context1中获取域数据key1的值是"+context.getAttribute("key1"));
      context.setAttribute("key1", "value1");
        System.out.println("context1中获取域数据key1的值是"+context.getAttribute("key1"));
        System.out.println("context1中获取域数据key1的值是"+context.getAttribute("key1"));
        System.out.println("context1中获取域数据key1的值是"+context.getAttribute("key1"));
    }

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

    }
}

  • 我们再创建一个ContextServlet2程序

我们在ContextServlet2中获取刚刚在ContextServlet1中存储的域数据key1的值

  • 注意:在这个过程中我们不要重启服务器或者是重新部署,只用将访问路径改成contextServlet2的路径即可(相当于先访问contextServlet1,然后访问contextServlt2)
package com.atguigu.servlet;

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

public class ContextServlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取Ser
        ServletContext context = getServletContext();
        System.out.println("context1中获取域数据key1的值是"+context.getAttribute("key1"));

    }

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

    }
}

  • 然后我们再次访问contextServlet1程序

重新部署和重启服务器比较类似,重新部署会将正在运行的工程停止,然后将工程重新运行,只是不用重启服务器而已

  • 对上面现象的解释

    一个web工程只有一个ServletContext对象,在web工程启动的时候回被创建,在web工程停止的时候会被销毁。在上面的案例中我们在ContextServlet1中存储了key=key1 value=value1。然后我们在ContextServlet2中进行访问,此时已经存在,并且是属于整个工程的,所以就可以访问了。

  • 当我们在重启服务器或者是重新部署的时候,我们的ServletContext对象会被销毁并且重新创建
    注意:整个工程只有一个ServletContext对象,我们在ContextServlt1和ContextServlet2中获取的是同一个ServletCdontext对象(可以打印输出看一下)

什么是Http协议

GET请求HTTP协议内容介绍

当发送GET请求的时候,我们可以使用IE浏览器可以看到HTTP协议的内容
user-agent:用户代理,指的是代理用户发送信息,指的是浏览器(我们使用浏览器发送信息)

POST请求HTTP协议内容介绍


GET和POST大部分的请求头是相同的

  • 假设我们下面的a.html程序向servlet程序提出请求(以post的方式)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--需要提交的表单:提交给servlet程序处理-->
    <form action="http://localhost:8080/tomcatTest/hello3" method="post">
        <input type="hidden" name="action" value="login"/>
        <input type="hidden" name="username" value="root"/>
      <input type="submit">
    </form>
</body>
</html>

常用的请求头

  • POST和GET有很多是重合的

哪些是GET请求 哪些是POST请求

  • 现在的问题是知道请求分为GET和POST两种类型,但是还是不清楚哪些请求是GET请求哪些请求是POST请

响应的HTTP协议介绍

  • 前面介绍的是请求时的HTTP协议,下面的是响应的时候的HTTP协议

常见的响应状态码说明

  • 500举例:servlet程序在处理请求出现异常,响应状态码将为500

MIME数据类型

谷歌浏览器和火狐浏览器如何查看HTTP协议

  • 在输入地址后按F12,并且查看network查看网络
  • 我们在浏览器中输入请求的内容,并点击回车(是GET请求)

  • 火狐和谷歌类型

Servlet2

HttpServletRequest类的介绍

HttpServletRequest类常用API演示

package com.atguigu.servlet;

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

public class RequestAPIServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       // i. getRequestURI() 获取请求的资源路径
        System.out.println("URI=>"+request.getRequestURI());
       // ii. getRequestURL() 获取请求的统一资源定位符(绝对路径)
        System.out.println("URL=>"+request.getRequestURL());
       // iii. getRemoteHost() 获取客户端的 ip 地址

        /*
        在IDEA中使用localhost访问时,得到的客户端ip地址是====>>127.0.0.1
        在IDEA中使用127.0.0.1访问时,得到的客户端ip地址是====>>127.0.0.1
        在IDEA中使用真实的ip访问时,得到的客户端ip地址是====>>真实的客户端Ip地址
         */
        System.out.println("客户端ip地址是=>"+request.getRemoteHost());
       // iv. getHeader() 获取请求头
        System.out.println("请求头user-agent=>"+request.getHeader("User-Agent"));
        //  vii. getMethod() 获取请求的方式 GET 或 POST
        System.out.println("请求的方式===>>"+request.getMethod());

    }


}

获取请求的参数值

  • 在Java Web开发中,请求参数主要指的是在HTTP请求中发送到服务器的数据

  • 1.我们先创建一个准备提交数据的form.html表单(get请求)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="http://localhost:8080/tomcatTest/paramServlet" method="get">

        用户名:<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
        <input type="submit">


    </form>
</body>
</html>
  • 我们新创建了一个servlet程序,访问地址是/paramServlet
package com.atguigu.servlet;

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

public class ParamServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求参数
        final String username = request.getParameter("username");
        final String password = request.getParameter("password");
        final String hobby = request.getParameter("hobby");
        System.out.println("用户名:"+username);
        System.out.println("密码:"+password);
        System.out.println("兴趣爱好:"+hobby);
    }


}

  • 提交后成功获取到了数据
  • 我们发现当我们进行了多选,即我们请求的参数有多个值,我们的`getParameter方法只能获取到一个值
  • servlet代码
package com.atguigu.servlet;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;

public class ParamServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取请求参数
        final String username = request.getParameter("username");
        final String password = request.getParameter("password");
        final String[] hobbies = request.getParameterValues("hobby");
        System.out.println("用户名:"+username);
        System.out.println("密码:"+password);
        System.out.println(Arrays.toString(hobbies));
    }


}



`

解决Post请求中文乱码问题

  • 现象展示:当我们post请求时,我们请求的参数出现中文时,将出现乱码问题
  • form.html文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="http://localhost:8080/tomcatTest/paramServlet" 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
        <input type="submit">


    </form>
</body>
</html>

  • servlet程序
package com.atguigu.servlet;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;

public class ParamServlet extends HttpServlet {


    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
        //获取请求参数
        final String username = request.getParameter("username");
        final String password = request.getParameter("password");
        final String[] hobbies = request.getParameterValues("hobby");
        System.out.println("用户名:"+username);
        System.out.println("密码:"+password);
        System.out.println(Arrays.toString(hobbies));
    }
}

  • 解决:我们需要使用一个API设置请求头的字符串为UTF-8就可以解决了
  • servlet程序
package com.atguigu.servlet;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;

public class ParamServlet extends HttpServlet {


    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
        //设置请求体的字符为UTF-8,从而解决POST请求的中文乱码问题
        request.setCharacterEncoding("UTF-8");
        //获取请求参数
        final String username = request.getParameter("username");
        final String password = request.getParameter("password");
        final String[] hobbies = request.getParameterValues("hobby");
        System.out.println("用户名:"+username);
        System.out.println("密码:"+password);
        System.out.println(Arrays.toString(hobbies));
    }
}

  • 乱码问题消失了

setCharacterEncoding()设置请求体的字符集 使用注意

  • 现象展示

  • 发现此时还是出现了乱码问题

  • 注意点结论
    我们设置请求头的API需要放置在获取请求参数的函数之前才有效

请求转发

  • 请求转发的操作步骤
  • 我们创建2个servlet程序:servlet1和servlet2,地址分别为/servlet1和/servlet2
  • servlet1
package com.atguigu.servlet;

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

public class Servlet1 extends HttpServlet {//柜台1
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取请求的参数(办事的材料)查看
        final String username = request.getParameter("username");
        System.out.println("在servlet1(柜台1)中参看参数(材料)"+username);
        //2.给材料盖一个章,并传递到servlet2(柜台2)去查看
        request.setAttribute("key1","柜台1的章");
        //3.问路:servlet2(柜台2)怎么走
        /*
        *请求转发必须要以/打头,/斜杠表示地址为:http://ip:port/工程名/ 映射到idea代码的web目录
         */
        final RequestDispatcher requestDispatcher = request.getRequestDispatcher("/servlet2");
        //4.走向servlet2(柜台2)
        requestDispatcher.forward(request,response);
    }


}

  • servlet2
package com.atguigu.servlet;

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

public class Servlet2 extends HttpServlet {//柜台2
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取请求的参数(办事的参数)查看
        final String username = request.getParameter("username");
        System.out.println("在servlet2(柜台2)中参看参数(材料)"+username);
        //2.查看柜台1 是否盖章
        final Object key1 = request.getAttribute("key1");//键要和servlet1中设置的一样
        System.out.println("柜台1是否有章:"+key1);
        //如果有章:servlet2处理自己的业务
        System.out.println("servlet2开始处理自己的业务");
    }

}

  • 注意:虽然我们进行了请求的转发,但是我地址还是servlet1的地址
  • 虽然访问了2个资源,但是属于一次请求
  • WEB-INF下面的内容不能直接访问,但是请求的转发可以进去访问
  • 演示:可以转发到WEB-INF目录下面的资源

  • 直接转发到了form.html

base标签的作用

现象展示

  • 我们让2个资源之间互相跳转(使用普通跳转)

  • c.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
这是a下面的b下面的c.html页面
<a href= "../../index.html">a/b/c.html</a>跳回首页</a>
</body>
</html>
  • index.hmtl
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
这是web下的index.html <br/>
<a href="a/b/c.html">a/b/c.html</a>
</body>
</html>

  • 点击跳转
  • 发现可以正常跳转

    下面我们使用请求转发来跳转
  • 我们先跳转到servlet程序,然后使用请求跳转到目标资源,然后从目标资源跳转到原资源
  • c.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
这是a下面的b下面的c.html页面<br/>
<a href= "../../index.html">请求转发:a/b/c.html</a>跳回首页</a><!--跳回原来的文件-->
</body>
</html>
  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
这是web下的index.html <br/>
<a href="http://localhost:8080/tomcatTest/forwardC">请求转发:a/b/c.html</a><!--跳转到forwardC的servlet程序-->
</body>
</html>
  • servlet程序
package com.atguigu.servlet;

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

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


}


  • 此时我们点击跳转后发现跳转失败了

    跳转失败原因分析

  • 所有的相对路径在工作的时候都会参照当前地址栏中的地址来跳转



发生这种情况的原因是相对路径跳转的时候参照的地址发生了变化

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--base标签设置页面相对路径工作时参照的地址
    href属性就是参数的地址值
    -->
    <base href="http://localhost:8080/tomcatTest/a/b/c.html">
</head>
<body>
这是a下面的b下面的c.html页面<br/>
<a href= "../../index.html">请求转发:a/b/c.html</a>跳回首页</a><!--跳回原来的文件-->
</body>
</html>

回顾java web中的路径

斜杠在web中的不同意义

HttpServletResponse类的作用

两个响应流的介绍

  • 如果同时使用的话,报错示例

如何给客户端回传字符串数据

  • servlet程序
package com.atguigu.servlet;

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

public class ResponseIOServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       //要求:往客户端回传 字符串数据
        final PrintWriter writer = response.getWriter();//获得字符流
      writer.write("response  contents");//往客户端回传数据
    }


}

解决响应的中文乱码(方案一)

  • 问题:当我们将前面回传的字符串改成中文,发现在显示的时候出现了乱码的问题
  • 但我们将服务器的字符集改成UTF-8,发现还是出现了中文乱码
    我们刚刚只是设置了服务器的字符集,但是当我们浏览器和服务器的字符集不统一的时候,也会出现显示乱码的问题
  • 我们的浏览器中可以设置字符集,但是如果都需要用户来设置,这就太麻烦了,所以我们需要通过程序进行设置
  • servlet程序
package com.atguigu.servlet;

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

public class ResponseIOServlet extends HttpServlet {
    @Override
    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字符集
        response.setHeader("content-type","text/html;charset=UTF-8");
       //要求:往客户端回传 字符串数据
        final PrintWriter writer = response.getWriter();//获得字符流
      writer.write("你好啊");//往客户端回传数据
    }


}

  • 此时就显示成功了

解决响应中文乱码(方案二)(推荐使用)

  • 使用setContentType("text/html;charset=UTF-8");同时设置服务器和浏览器的字符集
package com.atguigu.servlet;

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

public class ResponseIOServlet extends HttpServlet {
    @Override
    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字符集
      //  response.setHeader("content-type","text/html;charset=UTF-8");

        //他会同时设置客户端和服务器都使用UTF-8,还设置了响应头
        //注意:此方法一定要在获取流对象之前调用才有效
        response.setContentType("text/html;charset=UTF-8");
       //要求:往客户端回传 字符串数据
        final PrintWriter writer = response.getWriter();//获得字符流
      writer.write("你好啊");//往客户端回传数据
    }


}

注意:此方法一定要在获取流对象之前使用才有效

请求重定向

  • 请求重定向原理
  • 创建2个servlet程序:response1和response2地址分别为/response1和response2
  • response1
package com.atguigu.servlet;

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

public class Response1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("曾到此一游Response1");
       //1.设置响应码302,表示重定向(即表示已经搬迁)
        response.setStatus(302);
        //2.设置响应头,说明新的地址在哪里
        response.setHeader("Location","http://localhost:8080//tomcatTest/response2");

    }


}

  • response2
package com.atguigu.servlet;

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

public class Response2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //为了证明访问到了response2,向页面上打印一些内容
        response.getWriter().write("response2's result");
    }


}

  • 解释:不共享Request域中的数据

    此时我们的response2中不能得到response1中保存的对象
    因为tomcat每次收到请求就会把请求收集起来,封装成一个Resquest对象,这是2次请求,所以封装成了2个对象

  • 比如可以访问百度

  • 为社么不能访问WB-INF下的资源
    因为在第二次请求还是由浏览器发送的,而浏览器不能直接访问WEB-INF目录下,所以不能访问WEB-INF下的资源

请求重定向的第二种实现方案

  • 方案2
package com.atguigu.servlet;

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

public class Response1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("曾到此一游Response1");
       //1.设置响应码302,表示重定向(即表示已经搬迁)
      //  response.setStatus(302);
        //2.设置响应头,说明新的地址在哪里
       // response.setHeader("Location","http://localhost:8080//tomcatTest/response2");
       //方案二:直接设置跳转地址
        response.sendRedirect("http://localhost:8080//tomcatTest/response2");
    }


}