Servlet

215 阅读13分钟

Servlet

1、BS和CS的区别

CS:客户端服务器架构模式 优点:充分利用客户端机器的资源,减轻服务器的负荷(一部分安全要求不高的计算任务存储任务放在客户端执行,不需要把所有的计算和存储都在服务器执行,从而能够减轻服务器的压力,也能够减轻网络负荷) 举例:如果把所有的计算都放在服务器端计算,然后再发送给客户端,过程需要网络来传输,如果这部分直接放到客户端计算就可以直接得到结果了,因此可以减轻网络负荷 缺点:需要安装;升级维护成本较高

BS:浏览器服务器架构模式 优点:客户端不需要安装;维护成本较低 缺点:所有的计算和存储任务都是放在服务器端,服务器的负荷较重;再服务器计算完成之后把结果再传输给客户端,因此服务端和客户端会进行非常频繁的数据通信,从而网络负荷较重

2、什么是Servlet

Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器)端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web。

狂神的课程中更是把实现了servlet接口的java程序叫做servlet

3、Servlet的作用

首先了解到web开发的本质就一句话:客户端和服务器交换数据。于是使用Java的Socket套接字进行编程,去处理客户端来的tcp请求,经过编解码处理读取请求体,获取请求行,然后找到请求行对应的处理逻辑步入服务器的处理中,处理完毕把对应的结果返回给当前的Socket链接,响应完毕,关闭 Socket。

上述过程中, 建立连接、传输数据、关闭连接等过程是tomcat容器帮你做了这些事情, 而拿到请求行之后去找对应的url路由,这一部分就是Servlet来做的!更简单来说Servlet就是一段处理web请求的逻辑。

4、Servlet和Servlet容器的区别

首先需要了解容器的概念,生活中容器用来装水、装粮食,则对应的Servlet容器用来装类,装对象。

而编写的Servlet类为什么需要Servlet容器来管理?因为我们编写的Servlet类没有main()函数,不能独立运行,只能作为一个模块被载入到Servlet容器,然后由Servlet容器来实例化,并调用其中的方法。

一个动态页面对应一个Servlet类,开发一个动态页面就是编写一个Servlet类,当用户请求到达时,Servlet容器会根据配置文件(web.xml)来决定调用哪个类。

了解后可得到以下定义Servlet容器就是Servlet代码的运行环境(运行时),它除了实现Servlet规范定义的各种接口和类,为Servlet的运行提供底层支持,还需要管理由用户编写的Servlet类,比如实例化类(创建对象)、调用方法、销毁类等。

而本次学习采用的Servlet容器则是tomcat(tomcat是一个免费的开放源代码的Servlet容器,很多资料上也称为Web服务器,这里暂不对web容器和web服务器进行区别解释)

5、Servlet的三大作用域

request(请求):它的作用范围是一次请求和响应,是三个作用域中最小的。 session(会话):它的作用比request要大一点,一次会话过程中,它的作用域就一直存在,(默认是30分钟) servletcontext:它作用范围最大,作用于整个服务器中。(Application)

6、生命周期

Servlet生命周期分为四个部分: 实例化==>初始化==>执行处理==>销毁。

// JDK中的Servlet是一个接口
public interface Servlet {
    void init(ServletConfig var1) throws ServletException;
    ServletConfig getServletConfig();
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
    String getServletInfo();
    void destroy();
}

(1)生命周期:从出生到死亡的过程就是生命周期。对应Servlet中的三个方法:init(),service(),destroy() (2)默认情况下:第一次接受请求时,这个Servlet会进行实例化(调用构造方法;tomcat在底层用反射做的实例化)、初始化(调用init())、然后服务(调用service()) 然后从第二次请求开始,每一次都服务 当容器关闭时候,其中的所有的servlet实例会被销毁,调用销毁方法 (3)通过案例我们发现:Servlet实例tomcat只会创建一个,所有的请求都是这个实例来响应。 默认情况下,第一次请求时,tomcat才会去实例化,初始化,然后再服务。 这样的好处是什么?提高系统的启动速度。 这样的缺点是什么?第一次请求时,耗时较长 因此得出结论:如果需要提高系统的启动速度,当前默认情况就是这样。如果需要提高响应速度,我们应该设置Servlet的初始化时机——这里应该是讲反了把,提高启动速度应该是设置初始化时机,提高响应速度应该是默认 (4)Servlet的初始化时机: 默认是第一次接受请求时,实例化,初始化 我们可以通过来设置servlet启动的先后顺序,数字越小,启动越靠前,最小值0 (5)Servlet在容器中是:单例的、线程不安全的 单例:所有的请求都是同一个实例去响应 线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化 我们已经知道了servlet是线程不安全的,给我们的启发是:尽量的不要再servlet中定义成员变量。如果不得不定义成员变量,那么就不要修改成员变量的值,并且不要根据成员变量的值做一些逻辑判断

image.png

7、Servlet工作原理

Servlet处理请求的过程

1、首先简单解释一下Servlet接收和响应客户请求的过程:

客户发送一个请求,Servlet是调用service()方法对请求进行响应,service()方法中对请求的方式进行了匹配。选择调用doGet,doPost等这些方法,然后再进入对应的方法中调用逻辑层的方法,实现对客户的响应。在Servlet接口和GenericServlet中是没有doGet()、doPost()等等这些方法的,HttpServlet中定义了这些方法,但是都是返回error信息,所以,我们每次定义一个Servlet的时候,都必须实现doGet或doPost等这些方法。

2、每一个自定义的Servlet都必须实现Servlet的接口,Servlet接口中定义了五个方法,其中比较重要的三个方法涉及到Servlet的生命周期,分别是上文提到的init(),service(),destroy()方法。GenericServlet是一个通用的,不特定于任何协议的Servlet,它实现了Servlet接口。而HttpServlet继承于GenericServlet,因此HttpServlet也实现了Servlet接口。所以我们定义Servlet的时候只需要继承HttpServlet即可。

image.png

3、Servlet接口和GenericServlet是不特定于任何协议的,而HttpServlet是特定于HTTP协议的类,所以HttpServlet中实现了service()方法,并将请求ServletRequest、ServletResponse 强转为HttpRequest 和 HttpResponse。

客户端发送请求给服务器 容器根据请求及web.xml判断对应的Servlet是否存在,如果不存在则返回404。 容器根据请求及web.xml判断对应的Servlet是否已经被实例化,若是相应的Servlet没有被实例化,则容器将会加载相应的Servlet到Java虚拟机并实例化。 调用实例对象的service()方法,并开启一个新的线程去执行相关处理。调用servce方法,判断是调用doGet方法还是doPost方法。 业务完成后响应相关的页面发送给客户端。

简而言之:Servlet 容器接收到请求以后,会根据配置文件(web.xml)找到对应的 Servlet 类,将它加载并实例化,然后调用其中的方法来处理用户请求;处理结束后,Servlet 容器将处理结果再转交给 Web 服务器,由 Web 服务器将处理结果进行封装,以 HTTP 响应的形式发送给最终的用户。

image.png

image.png

image.png

8、小结

(1)继承关系:HttpServlet -> GenericServlet -> Servlet (2)Servlet中的核心方法:init(),service(),destroy() (3)服务方法:当有请求过来时,service方法会自动响应(tomcat容器调用)在HttpServlet中会分析请求的方式:到底是get、post、head还是delete等等 然后再去调用的是哪个do开头的方法 那么在HttpServlet中这些do方法默认都是405的实现风格-要我们子类去实现对应的方法,否则默认会报405错误 (4)因此,我们在新建Servlet时,我们才会去考虑请求方法,从而决定重写哪个do方法(父类中的方法就是报405错误) (5)Tomcat与Servlet如何工作的?

  1. Web Client 向Servlet容器发出Http请求;
  2. Servlet容器接收Web Client 的请求; 3.Servlet容器创建一个Http Request对象,将Web Client请求信息封装到这个对象中; 4.Servlet容器创建一个HttpResponse对象; 5.Servlet容器调用HttpServlet对象的Service方法,把HttpRequest对象与HttpResponse对象作为参数传给HttpServlet对象; 6.HttpServlet调用HttpRequest对象相关方法获取Http请求信息; 7.HttpServlet调用HttpResponse对象的相关方法生成响应数据; 8.Servlet容器把HttpServlet的响应结果传给WebClient;

9、实践

9.1 Mapping问题

1、一个Servlet可以指定一个映射路径

2、一个Servlet可以指定多个映射路径

<!--Servlet的请求路径-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello2</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello3</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello4</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello5</url-pattern>
</servlet-mapping>

3、一个Servlet可以指定通用映射路径

// 这种写法优先级很高,直接访问servlet了,不去访问index.jsp了
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello5/*</url-pattern>
</servlet-mapping>

4、指定一些后缀或者前缀等等

    <!--可以自定义后缀实现请求映射-->
    <!--只要以这个结尾即可即/hello.wang都可以-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>*.wang</url-pattern>
    </servlet-mapping>

image.png

image.png

image.png

5、优先级问题

制定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求

public class ErrorServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 这里可以写成resp.setContentType("text/html;charset=utf-8");合并成一行
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
​
        PrintWriter writer = resp.getWriter();
        writer.print("<h1>404</h1>");
    }
​
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
public class HelloServlet extends HttpServlet {
​
    // 由于get或者post只是请求实现的不同的方式,可以互相调用,业务逻辑都一样
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
​
        System.out.println("进入了doGet方法");
        //ServletOutputStream outputStream = resp.getOutputStream();
        // 响应流
        PrintWriter writer = resp.getWriter();
        writer.print("Hello,Serlvet");
    }
​
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
<!--注册Servlet-->
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.wang.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的请求路径-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
<!--404-->
<servlet>
    <servlet-name>error</servlet-name>
    <servlet-class>com.wang.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>error</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

image.png

image.png

9.2 ServletContext

web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用

1、共享数据:

我在这个Servlet中保存的数据,可以在另外一个Servlet中拿到

// 放置数据的类
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
​
//        this.getInitParameter();初始化参数
//        this.getServletConfig();Servlet配置
//        this.getServletContext();Servlet上下文
​
        ServletContext context = this.getServletContext();
​
        String username = "秦疆"; // 数据
        // 将一个数据保存在了ServletContext,名字为username,值为username
        context.setAttribute("username", username);
        System.out.println("hello");
    }
}
// 读取数据的类
public class GetServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
​
        ServletContext context = this.getServletContext();
        String username = (String) context.getAttribute("username");
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().print("名字" + username);
​
    }
​
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
  <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
​
  <servlet>
    <servlet-name>getc</servlet-name>
    <servlet-class>com.kuang.servlet.GetServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>getc</servlet-name>
    <url-pattern>/getc</url-pattern>
  </servlet-mapping></web-app>

image.png

image.png

2、获取初始化参数

<!--配置一些web应用初始化参数-->
<context-param>
  <param-name>url</param-name>
  <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
<servlet>
  <servlet-name>gp</servlet-name>
  <servlet-class>com.kuang.servlet.ServletDemo03</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>gp</servlet-name>
  <url-pattern>/gp</url-pattern>
</servlet-mapping>
public class ServletDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String url = context.getInitParameter("url");
        resp.getWriter().print(url);
    }
​
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

3、请求转发(用的少了解)

请求的是sd4,但是显示的页面是gp,转发的时候路径不会发生变化,之后的重定向路径会变

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext context = this.getServletContext();
    System.out.println("进入了sd4");
    // 获得请求转发,里面参数就是转发的请求路径
    // 这里的路径是相对路径
    RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");
    // 调用forward实现请求转发
    requestDispatcher.forward(req, resp);
}
<servlet>
  <servlet-name>sd4</servlet-name>
  <servlet-class>com.kuang.servlet.ServletDemo04</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>sd4</servlet-name>
  <url-pattern>/sd4</url-pattern>
</servlet-mapping>

image.png

image.png

转发是站内请求,重定向是站外请求

image-20220512115120111

image.png

4、读取资源文件

Properties

  • 在java目录下新建properties
  • 在resources目录下新建properties

发现:都被打包到了同一路径下:classes,我们俗称这个路径为classpath

<servlet>
  <servlet-name>sd5</servlet-name>
  <servlet-class>com.kuang.servlet.ServletDemo05</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>sd5</servlet-name>
  <url-pattern>/sd5</url-pattern>
</servlet-mapping>
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 此处是把资源变成流,因为prop.load需要流
    InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/kuang/servlet/aa.properties");
    Properties prop = new Properties();
    prop.load(is);
    String user = prop.getProperty("username");
    String pwd = prop.getProperty("password");
​
    resp.getWriter().print(user + ":" + pwd);
}

maven由于他的约定大于配置,我们之后可能会遇到我们写的配置文件,无法被导出或者生效的问题,解决方案:

<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <includes>
        <include>**/*.properties</include>
        <include>**/*.xml</include>
      </includes>
      <filtering>false</filtering>
    </resource>
    <resource>
      <directory>src/main/java</directory>
      <includes>
        <include>**/*.properties</include>
        <include>**/*.xml</include>
      </includes>
      <filtering>false</filtering>
    </resource>
  </resources>
</build>

image.png

image.png

#classespath是类路径,java和resources

外部应用就是classes

9.3 HttpServletResponse

web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的一个HttpServletResponse;

  • 如果要获取客户端请求过来的参数:找HttpServletRequest
  • 如果要给客户端响应一些信息:找HttpServletResponse

1、简单分类

负责向浏览器发送数据的方法

// 其他流
ServletOutputStream getOutputStream() throws IOException;
// 中文
PrintWriter getWriter() throws IOException;

负责向浏览器发送响应头的方法

void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentType(String var1);
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);

响应的状态码

int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;

2、常见应用

  1. 向浏览器输出消息

  2. 下载文件

    1. 要获取下载文件的路径
    2. 下载的文件名是啥
    3. 设置想办法让浏览器能够指出下载我们需要的东西
    4. 获取下载文件的输入流
    5. 创建缓冲区
    6. 获取OutputStream对象
    7. 将FileOutputStream流写入到buffer缓冲区
    8. 使用OutputStream将缓冲区中的数据输出到客户端

3、验证码功能

image.png

<?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"
         metadata-complete="true">
    
    <servlet>
        <servlet-name>ImageServlet</servlet-name>
        <servlet-class>com.wang.servlet.ImageServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ImageServlet</servlet-name>
        <url-pattern>/image</url-pattern>
    </servlet-mapping>
</web-app>
public class ImageServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
​
        // 如何让浏览器5秒自动刷新一次
        resp.setHeader("refresh", "3");
​
        // 在内存中创建一个图片
        BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
        // 得到图片
        Graphics2D g = (Graphics2D) image.getGraphics();
        // 设置图片的背景颜色
        g.setColor(Color.BLACK);
        g.fillRect(0,0,80,20);
        // 给图片写数据
        g.setColor(Color.BLUE);
        g.setFont(new Font(null,Font.BOLD,20));
        g.drawString(makeNum(),0,20);
​
        // 告诉浏览器这个请求用图片的方式打开
        resp.setContentType("image/jpeg");
        // 网站存在缓存,不让浏览器缓存
        resp.setDateHeader("expires", -1);
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Pragma","no-cache");
​
        // 把图片写给浏览器
        ImageIO.write(image,"jpg",resp.getOutputStream());
​
    }
    // 生成随机数
    private String makeNum(){
        Random random = new Random();
        String num = random.nextInt(9999999) + "";
        StringBuffer sb = new StringBuffer();
        // fo循环是保证这个数一定是7位的
        for (int i = 0; i < 7 - num.length(); i++) {
            sb.append("0");
        }
        num = sb.toString() + num;
        return num;
    }
​
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

image.png

4、实现重定向

image.png

一个web资源B收到客户端A请求后,B他会通知客户端A去访问另外一个web资源C,这个过程叫重定向

常见场景:

  • 用户登录

    void sendRedirect(String var1) throws IOException;
    

image.png

image.png

// 所以此处要加上/res
resp.sendRedirect("/res/image");
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 重定向的原理
//        resp.setHeader("Location", "/res/image");
//        resp.setStatus(302);
​
        // 所以此处要加上/res
        resp.sendRedirect("/res/image");
    }

面试题:请你聊聊重定向和转发的区别?

相同点

  • 页面都会实现跳转

不同点

  • 请求转发的时候,url不会产生变化
  • 重定向时候,url地址栏会发生变化

总结: 1.转发是一次请求,重定向是两次 2.转发不会改变url,重定向会改变 3.请求转发是在服务器内部完成的,而重定向是在客户端完成的 4.转发的url必须是当前web工程内部的地址,重定向可以是任意地址

image.png

image.png

image.png

image.png

9.4 HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法获得客户端的所有信息

image.png

1、获取前端传递的参数

image.png

2、请求转发

之前讲过一种转发和一种重定向页面

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext context = this.getServletContext();
    System.out.println("进入了sd4");
    // 获得请求转发,里面参数就是转发的请求路径
    RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");
    // 调用forward实现请求转发
    requestDispatcher.forward(req, resp);
}
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 重定向的原理
//        resp.setHeader("Location", "/res/image");
//        resp.setStatus(302);
​
        // 所以此处要加上/res!!!!!!!!
        resp.sendRedirect("/res/image");
    }

image-20220513152500248

image.png 以下为常用的请求转发方式

public class LoginServlet extends HttpServlet {
​
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
​
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobbys = req.getParameterValues("hobbys");
        System.out.println("***********************");
        // 后台接受中文乱码问题
        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(hobbys));
        System.out.println("***********************");
        // 通过请求转发
        req.getRequestDispatcher("/success.jsp").forward(req,resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录i</title>
</head>
<body>
<h1>登录</h1><div style="text-align: center">
    <%--这里表单表示的意思是以post方式提交表单,提交到我们的login请求--%>
    <form action="${pageContext.request.contextPath}/login" method="post">
        用户名:<input type="text" name="username"><br>
        密码:<input type="password" name="password"><br>
        爱好:
        <input type="checkbox" name="hobbys" value="女害">怒海
        <input type="checkbox" name="hobbys" value="代码">代码
        <input type="checkbox" name="hobbys" value="唱歌">唱歌
        <input type="checkbox" name="hobbys" value="电影">电影
        <br>
        <input type="submit">
    </form>
</div>
</body>
</html>

image.png

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>登陆成功</h1></body>
</html>

image.png

image.png

image.png

image.png

面试题:请你聊聊重定向和转发的区别?

相同点

  • 页面都会实现跳转

不同点

  • 请求转发的时候,url不会产生变化:307
  • 重定向时候,url地址栏会发生变化:302

\